首页 > 解决方案 > 从 Delphi 启动 Windows Optimize 应用程序 (Windows 10)

问题描述

我们有一个旧版 Delphi 7 应用程序,它启动 Windows Defrag 和屏幕键盘应用程序,如下所示:

// 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,但更新代码没有帮助。osk.exe在 Windows 10 上仍会调用屏幕键盘。

这两个应用程序都可以手动/直接从命令行启动,或者在 Windows 资源管理器中双击它们。

我的怀疑是 Windows 安全性阻止我的应用程序从 启动任何东西,因为我可以从和从C:\Windows\System32启动其他几个应用程序。Program FilesC:\Windows

任何人都可以帮忙吗?

标签: windowsdelphidelphi-7

解决方案


Delphi 7 只生成 32 位应用程序,没有生成 64 位应用程序的选项(在 XE2 中添加)。

%WINDIR%\System32从在 64 位系统上运行的 32 位应用程序访问路径受 WOW64 的文件系统重定向器的约束,它将以静默方式将 64 位System32文件夹的请求重定向到 32 位SysWOW64文件夹。

您尝试运行的应用程序可能仅存在于 64 位System32文件夹中,而不存在于 32 位SysWOW64文件夹中。

为避免重定向,您需要:

  • 替换为路径中System32的特殊别名(即),该别名仅在 WOW64 下运行时有效,因此您必须在运行时通过以下方式动态检测:Sysnative'C:\Windows\Sysnative\osk.exe'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(),然后在完成后通过重新启用它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;
    

更新osk.exe:话虽如此,事实证明在启用 UAC 时不允许运行在 WOW64 下运行的 32 位进程:

Delphi - 屏幕键盘 (osk.exe) 在 Win32 上工作但在 Win64 上失败

因此,当应用程序在 WOW64 下运行时,您必须创建一个 64 位辅助进程以osk.exe代表您的应用程序启动。


推荐阅读