从 Delphi 启动 Windows 优化应用程序 (Windows 10)
Launch Windows Optimize application (Windows 10) from Delphi
我们有一个遗留的 Delphi 7 应用程序启动 Windows 碎片整理和屏幕键盘应用程序,如下所示:
// Defragmentation application
ShellExecute(0, 'open', PChar('C:\Windows\System32\dfrg.msc'), nil, nil, SW_SHOWNORMAL);
// On-screen keyboard
ShellExecute(0, 'open', PChar('C:\Windows\System32\osk.exe'), nil, nil, SW_SHOWNORMAL);
两者都在 Windows XP 上工作,但在 Windows 10 上失败。我发现碎片整理应用程序的名称已更改为 dfrgui.exe
,但更新代码没有帮助。屏幕键盘在 Windows 10.
上仍称为 osk.exe
这两个应用程序都可以手动/直接从命令行启动,或者在 Windows 资源管理器中双击它们。
我怀疑 Windows 安全性阻止我的应用程序从 C:\Windows\System32
启动任何东西,因为我可以从 Program Files
和 C:\Windows
启动其他几个应用程序。
有人能帮忙吗?
Delphi 7 只生成 32 位应用程序,没有生成 64 位应用程序的选项(这是在 XE2 中添加的)。
在 64 位系统上从 32 位应用 运行ning 访问 %WINDIR%\System32
下的路径受 WOW64 的 文件约束
系统重定向器,它将以静默方式将对 64 位 System32
文件夹的请求重定向到 32 位 SysWOW64
文件夹。
很可能,您尝试 运行 的应用程序仅存在于 64 位 System32
文件夹中,而不存在于 32 位 SysWOW64
文件夹中。
要避免重定向,您需要:
将System32
替换为您路径中的特殊Sysnative
别名(即'C:\Windows\Sysnative\osk.exe'
),这仅在WOW64下运行ning时有效,所以你必须在 运行 时间通过 IsWow64Process()
:
动态检测
function GetSystem32Folder: string;
var
Folder: array[0..MAX_PATH] of Char;
IsWow64: BOOL;
begin
Result := '';
if IsWow64Process(GetCurrentProcess(), @IsWow64) and IsWow64 then
begin
SetString(Result, Folder, GetWindowsDirectory(Folder, Length(Folder)));
if Result <> '' then
Result := IncludeTrailingPathDelimiter(Result) + 'Sysnative' + PathDelim;
end else
begin
SetString(Result, Folder, GetSystemDirectory(Folder, Length(Folder)));
if Result <> '' then
Result := IncludeTrailingPathDelimiter(Result);
end;
end;
function RunDefrag: Boolean;
var
SysFolder: string;
Res: Integer;
begin
SysFolder := GetSystem32Folder;
Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrgui.exe'), nil, nil, SW_SHOWNORMAL));
if Res = ERROR_FILE_NOT_FOUND then
Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrg.msc'), nil, nil, SW_SHOWNORMAL));
Result := (Res = 0);
end;
function RunOnScreenKeyboard: Boolean;
begin
Result := (ShellExecute(0, nil, PChar(GetSystem32Folder + 'osk.exe'), nil, nil, SW_SHOWNORMAL) = 0);
end;
完成后通过 Wow64DisableWow64FsRedirection()
, and then re-enable it via Wow64RevertWow64FsRedirection()
暂时禁用重定向器:
function GetSystem32Folder: string
var
Folder: array[0..MAX_PATH] of Char;
begin
SetString(Result, Folder, GetSystemDirectory(Folder, Length(Folder)));
if Result <> '' then
Result := IncludeTrailingPathDelimiter(Result);
end;
function RunDefrag: Boolean;
var
SysFolder: string;
OldState: Pointer;
Res: Integer;
begin
Wow64DisableWow64FsRedirection(@OldState);
try
SysFolder := GetSystem32Folder;
Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrgui.exe'), nil, nil, SW_SHOWNORMAL));
if Res = ERROR_FILE_NOT_FOUND then
Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrg.msc'), nil, nil, SW_SHOWNORMAL));
Result := Res = 0;
finally
Wow64RevertWow64FsRedirection(OldState);
end;
end;
function RunOnScreenKeyboard: Boolean;
var
OldState: Pointer;
begin
Wow64DisableWow64FsRedirection(@OldState);
try
Result := (ShellExecute(0, nil, PChar(GetSystem32Folder + 'osk.exe'), nil, nil, SW_SHOWNORMAL) = 0);
finally
Wow64RevertWow64FsRedirection(OldState);
end;
end;
Update: 话虽这么说,结果发现WOW64下的32位进程运行ning是不允许运行 osk.exe
启用 UAC 时:
Delphi - On Screen Keyboard (osk.exe) works on Win32 but fails on Win64
因此,当应用程序在 WOW64 下 运行ning 时,您必须创建一个 64 位辅助进程来代表您的应用程序启动 osk.exe
。
对 Remy Lebeau 的回答的一个小补充:
如果 Wow64DisableWow64FsRedirection
在您的 Delphi 版本中不可用,and/or 如果您不确定您的目标平台是否支持这个 API,您可以使用以下动态调用函数的代码示例:
https://www.delphipraxis.net/155861-windows-7-64bit-redirection.html
function ChangeFSRedirection(bDisable: Boolean): Boolean;
type
TWow64DisableWow64FsRedirection = Function(Var Wow64FsEnableRedirection: LongBool): LongBool; StdCall;
TWow64EnableWow64FsRedirection = Function(var Wow64FsEnableRedirection: LongBool): LongBool; StdCall;
var
hHandle: THandle;
Wow64DisableWow64FsRedirection: TWow64DisableWow64FsRedirection;
Wow64EnableWow64FsRedirection: TWow64EnableWow64FsRedirection;
Wow64FsEnableRedirection: LongBool;
begin
Result := false;
try
hHandle := GetModuleHandle('kernel32.dll');
@Wow64EnableWow64FsRedirection := GetProcAddress(hHandle, 'Wow64EnableWow64FsRedirection');
@Wow64DisableWow64FsRedirection := GetProcAddress(hHandle, 'Wow64DisableWow64FsRedirection');
if bDisable then
begin
if (hHandle <> 0) and (@Wow64DisableWow64FsRedirection <> nil) then
begin
Result := Wow64DisableWow64FsRedirection(Wow64FsEnableRedirection);
end;
end else
begin
if (hHandle <> 0) and (@Wow64EnableWow64FsRedirection <> nil) then
begin
Result := Wow64EnableWow64FsRedirection(Wow64FsEnableRedirection);
Result := True;
end;
end;
Except
end;
end;
我们有一个遗留的 Delphi 7 应用程序启动 Windows 碎片整理和屏幕键盘应用程序,如下所示:
// Defragmentation application
ShellExecute(0, 'open', PChar('C:\Windows\System32\dfrg.msc'), nil, nil, SW_SHOWNORMAL);
// On-screen keyboard
ShellExecute(0, 'open', PChar('C:\Windows\System32\osk.exe'), nil, nil, SW_SHOWNORMAL);
两者都在 Windows XP 上工作,但在 Windows 10 上失败。我发现碎片整理应用程序的名称已更改为 dfrgui.exe
,但更新代码没有帮助。屏幕键盘在 Windows 10.
osk.exe
这两个应用程序都可以手动/直接从命令行启动,或者在 Windows 资源管理器中双击它们。
我怀疑 Windows 安全性阻止我的应用程序从 C:\Windows\System32
启动任何东西,因为我可以从 Program Files
和 C:\Windows
启动其他几个应用程序。
有人能帮忙吗?
Delphi 7 只生成 32 位应用程序,没有生成 64 位应用程序的选项(这是在 XE2 中添加的)。
在 64 位系统上从 32 位应用 运行ning 访问 %WINDIR%\System32
下的路径受 WOW64 的 文件约束
系统重定向器,它将以静默方式将对 64 位 System32
文件夹的请求重定向到 32 位 SysWOW64
文件夹。
很可能,您尝试 运行 的应用程序仅存在于 64 位 System32
文件夹中,而不存在于 32 位 SysWOW64
文件夹中。
要避免重定向,您需要:
将
动态检测System32
替换为您路径中的特殊Sysnative
别名(即'C:\Windows\Sysnative\osk.exe'
),这仅在WOW64下运行ning时有效,所以你必须在 运行 时间通过IsWow64Process()
:function GetSystem32Folder: string; var Folder: array[0..MAX_PATH] of Char; IsWow64: BOOL; begin Result := ''; if IsWow64Process(GetCurrentProcess(), @IsWow64) and IsWow64 then begin SetString(Result, Folder, GetWindowsDirectory(Folder, Length(Folder))); if Result <> '' then Result := IncludeTrailingPathDelimiter(Result) + 'Sysnative' + PathDelim; end else begin SetString(Result, Folder, GetSystemDirectory(Folder, Length(Folder))); if Result <> '' then Result := IncludeTrailingPathDelimiter(Result); end; end; function RunDefrag: Boolean; var SysFolder: string; Res: Integer; begin SysFolder := GetSystem32Folder; Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrgui.exe'), nil, nil, SW_SHOWNORMAL)); if Res = ERROR_FILE_NOT_FOUND then Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrg.msc'), nil, nil, SW_SHOWNORMAL)); Result := (Res = 0); end; function RunOnScreenKeyboard: Boolean; begin Result := (ShellExecute(0, nil, PChar(GetSystem32Folder + 'osk.exe'), nil, nil, SW_SHOWNORMAL) = 0); end;
完成后通过
Wow64DisableWow64FsRedirection()
, and then re-enable it viaWow64RevertWow64FsRedirection()
暂时禁用重定向器:function GetSystem32Folder: string var Folder: array[0..MAX_PATH] of Char; begin SetString(Result, Folder, GetSystemDirectory(Folder, Length(Folder))); if Result <> '' then Result := IncludeTrailingPathDelimiter(Result); end; function RunDefrag: Boolean; var SysFolder: string; OldState: Pointer; Res: Integer; begin Wow64DisableWow64FsRedirection(@OldState); try SysFolder := GetSystem32Folder; Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrgui.exe'), nil, nil, SW_SHOWNORMAL)); if Res = ERROR_FILE_NOT_FOUND then Res := Integer(ShellExecute(0, nil, PChar(SysFolder + 'dfrg.msc'), nil, nil, SW_SHOWNORMAL)); Result := Res = 0; finally Wow64RevertWow64FsRedirection(OldState); end; end; function RunOnScreenKeyboard: Boolean; var OldState: Pointer; begin Wow64DisableWow64FsRedirection(@OldState); try Result := (ShellExecute(0, nil, PChar(GetSystem32Folder + 'osk.exe'), nil, nil, SW_SHOWNORMAL) = 0); finally Wow64RevertWow64FsRedirection(OldState); end; end;
Update: 话虽这么说,结果发现WOW64下的32位进程运行ning是不允许运行 osk.exe
启用 UAC 时:
Delphi - On Screen Keyboard (osk.exe) works on Win32 but fails on Win64
因此,当应用程序在 WOW64 下 运行ning 时,您必须创建一个 64 位辅助进程来代表您的应用程序启动 osk.exe
。
对 Remy Lebeau 的回答的一个小补充:
如果 Wow64DisableWow64FsRedirection
在您的 Delphi 版本中不可用,and/or 如果您不确定您的目标平台是否支持这个 API,您可以使用以下动态调用函数的代码示例:
https://www.delphipraxis.net/155861-windows-7-64bit-redirection.html
function ChangeFSRedirection(bDisable: Boolean): Boolean;
type
TWow64DisableWow64FsRedirection = Function(Var Wow64FsEnableRedirection: LongBool): LongBool; StdCall;
TWow64EnableWow64FsRedirection = Function(var Wow64FsEnableRedirection: LongBool): LongBool; StdCall;
var
hHandle: THandle;
Wow64DisableWow64FsRedirection: TWow64DisableWow64FsRedirection;
Wow64EnableWow64FsRedirection: TWow64EnableWow64FsRedirection;
Wow64FsEnableRedirection: LongBool;
begin
Result := false;
try
hHandle := GetModuleHandle('kernel32.dll');
@Wow64EnableWow64FsRedirection := GetProcAddress(hHandle, 'Wow64EnableWow64FsRedirection');
@Wow64DisableWow64FsRedirection := GetProcAddress(hHandle, 'Wow64DisableWow64FsRedirection');
if bDisable then
begin
if (hHandle <> 0) and (@Wow64DisableWow64FsRedirection <> nil) then
begin
Result := Wow64DisableWow64FsRedirection(Wow64FsEnableRedirection);
end;
end else
begin
if (hHandle <> 0) and (@Wow64EnableWow64FsRedirection <> nil) then
begin
Result := Wow64EnableWow64FsRedirection(Wow64FsEnableRedirection);
Result := True;
end;
end;
Except
end;
end;