はじめに
WOW64 環境下 (64bit Windows で 32bit アプリケーションを実行) においては、特定の条件でパスがリダイレクトされる事があります。
このため、明らかにパスが存在するのに「ファイルが存在しません」と言われる事があります。
リダイレクトの回避
WOW64 によるリダイレクトを回避するには、Wow64DisableWow64FsRedirection()
API と Wow64RevertWow64FsRedirection()
API のペアを使います。
以前は Wow64EnableWow64FsRedirection()
という単一の API でリダイレクトのオンオフを切り替えていましたが、これだとネストさせる事ができないので、現行の仕様に変更された模様です。
See also:
- Wow64DisableWow64FsRedirection() (learn.microsoft.com)
- Wow64RevertWow64FsRedirection() (learn.microsoft.com)
- Wow64EnableWow64FsRedirection() (learn.microsoft.com)
コード
Delphi で WOW64 によるリダイレクトを回避するためのルーチンを用意しました。先述の API のラッパーです。
unit uWOW64;
{$IFDEF MSWINDOWS}{$IFDEF WIN32}
{$DEFINE TARGET_WINDOWS}
{$ENDIF}{$ENDIF}
interface
{$IFDEF TARGET_WINDOWS}
uses
Windows;
{$ENDIF}
function IsWOW64: Boolean;
function DisableWow64FsRedirection(var OldValue: LongBool): LongBool;
function RevertWow64FsRedirection(const OldValue: LongBool): LongBool;
{$IFDEF TARGET_WINDOWS}
var
IsWow64Process: function(hProcess: THandle; var Wow64Process: BOOL): BOOL stdcall;
Wow64DisableWow64FsRedirection: function (out OldValue: LongBool): LongBool; stdcall;
Wow64RevertWow64FsRedirection: function (const OldValue: LongBool): LongBool; stdcall;
Wow64EnableWow64FsRedirection: function(const Wow64FsEnableRedirection: LongBool): LongBool; stdcall;
{$ENDIF}
implementation
function IsWOW64: Boolean;
{$IFDEF TARGET_WINDOWS}
var
Flg: BOOL;
begin
result := False;
@IsWow64Process := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'IsWow64Process');
if @IsWow64Process <> nil then
if IsWow64Process(GetCurrentProcess, Flg) then
result := Flg;
{$ELSE}
begin
result := False;
{$ENDIF}
end;
function DisableWow64FsRedirection(var OldValue: LongBool): LongBool;
begin
result := False;
if not IsWOW64 then
Exit;
{$IFDEF TARGET_WINDOWS}
@Wow64DisableWow64FsRedirection := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'Wow64DisableWow64FsRedirection');
if @Wow64DisableWow64FsRedirection <> nil then
result := Wow64DisableWow64FsRedirection(OldValue)
else
begin
@Wow64EnableWow64FsRedirection := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'Wow64EnableWow64FsRedirection');
if @Wow64EnableWow64FsRedirection <> nil then
result := Wow64EnableWow64FsRedirection(False);
end;
{$ENDIF}
end;
function RevertWow64FsRedirection(const OldValue: LongBool): LongBool;
begin
result := False;
if not IsWOW64 then
Exit;
{$IFDEF TARGET_WINDOWS}
@Wow64RevertWow64FsRedirection := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'Wow64RevertWow64FsRedirection');
if @Wow64RevertWow64FsRedirection <> nil then
result := Wow64RevertWow64FsRedirection(OldValue)
else
begin
@Wow64EnableWow64FsRedirection := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'Wow64EnableWow64FsRedirection');
if @Wow64EnableWow64FsRedirection <> nil then
result := Wow64EnableWow64FsRedirection(True);
end;
{$ENDIF}
end;
end.
あるのかどうかはわかりませんが Wow64EnableWow64FsRedirection()
しか使えない環境にも対応しています。但し、この場合にはネストができません。
使い方
使い方は、uses に uWOW64 を記述して、DisableWow64FsRedirection()
と RevertWow64FsRedirection()
で括るだけです。ステートを保存するための OldValue (LongBool 型) を引数として渡す必要があります。ネストする際には別の変数を用意してください。
DisableWow64FsRedirection()
と RevertWow64FsRedirection()
は WOW64 環境下でしか動作しないので、32bit / 64bit 両用のコードであっても条件コンパイル用の指令を記述する必要はありません。
uses
..., uWOW64;
var
OldValue: LongBool;
...
DisableWow64FsRedirection(OldValue);
try
// リダイレクトが予想される処理 (ShellExecute など)
finally
RevertWow64FsRedirection(OldValue);
end;
リダイレクトの具体的な例
Windows 10/11 に環境に WSL/WSL2 環境があれば、[ファイル名を指定して実行] (〔Win〕+〔R〕) で wsl.exe ~
を実行してみてください。
普通に WSL/WSL2 が起動したかと思います。では、32bit アプリケーションで次のコードを試してみてください。
uses
..., Winapi.ShellAPI;
ShellExecute(0, 'open', 'wsl.exe' , '~', nil, SW_SHOWNORMAL);
WSL/WSL2 は起動しなかったと思います。64bit アプリケーションでは正しく起動できますし、wsl.exe
を explorer.exe
にすれば Explorer はちゃんと起動するハズです。
次のようにすれば WSL/WSL2 を 32bit アプリケーションから起動できるようになります。
uses
..., Winapi.ShellAPI, uWOW64;
var
OldValue: LongBool;
...
DisableWow64FsRedirection(OldValue);
try
ShellExecute(0, 'open', 'wsl.exe' , '~', nil, SW_SHOWNORMAL);
finally
RevertWow64FsRedirection(OldValue);
end;
おわりに
そういえば、FMX.Helpers.Win
名前空間 1 には TWow64Redirection
というラッパークラスがあるのですが、何故 WinApi
名前空間ではないのでしょうね?VCL / Firemonkey 問わずに使えそうな気がするのですが?
See also:
-
2023/04/18 現在ドキュメントがありません。 ↩