从 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 FilesC:\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;