Delphi:如何确定应用程序是不是在 Win32 / Win64 下运行并在 64 位上自动启动 64 位版本?

Posted

技术标签:

【中文标题】Delphi:如何确定应用程序是不是在 Win32 / Win64 下运行并在 64 位上自动启动 64 位版本?【英文标题】:Delphi: How to determine if an application is running under Win32 / Win64 and launch the 64-bit version automatically if on 64-bit?Delphi:如何确定应用程序是否在 Win32 / Win64 下运行并在 64 位上自动启动 64 位版本? 【发布时间】:2018-11-16 21:10:36 【问题描述】:

情况

我打算将我的 Delphi (XE6) GUI 应用程序的 32 位和 64 位版本捆绑在一个 Zip 存档中。

注意:在这个问题中,我们假设用户提取了整个存档,即没有直接从存档管理器运行可执行文件。

所以,假设我们已经提取了存档并有两个文件:

    program.exe

    program64.exe

明确地说,命名约定是 32 位版本的名称中除了程序名称之外没有其他任何内容,而 64 位版本的程序名称与 64 后缀完全相同。

意图

如果用户在 64 位 Windows 机器上运行 32 位版本,我希望它能够检测到它,并自行关闭并运行 64 位版本。

注意

在我自己的答案中,我将记录我在使用代码期间的所有发现,随时添加更好的答案或替代方案,如果它确实做出了一些贡献,我肯定会赞成。也许更重要的是,请对您发现错误的特定部分发表评论。

【问题讨论】:

为什么不首先使用适当的安装程序来安装适当的版本? 64位版本连32位机器都不能用,为什么还要分发? @JerryDodge 因为它是一个很小的应用程序。无需安装程序。当然你可能会争辩说,64位版本是不必要的,我只是想要它。 【参考方案1】:

启动其他可执行文件

首先,我们需要一个能够启动其他可执行文件的函数,下面是我非常通用的示例,您可以根据需要使用它来启动几乎任何其他可执行文件:

function LaunchExecutableFile(const ExecutableFilePath, Parameters: string; const ShowCmd: Integer): Boolean;
begin
  Result := Winapi.ShellAPI.ShellExecute(Application.MainFormHandle, 'open', PChar(StringFunctions.DoubleQuoteStr(ExecutableFilePath)), PChar(Parameters), nil, ShowCmd) > 32;
end;

注意事项:

    我特意添加了Winapi.ShellAPI... 等可选命名空间,以便您准确了解这些函数的定义位置。

    定义了32个错误码,这就是为什么如果ShellExecute的结果大于32,函数返回True

    我定义了函数DoubleQuoteStr,因为如果路径中有空格,系统会在每个空格分隔的文件中查找文件,因此路径错误。这是一个非常简单的功能,完全是可选的,只是优化。这个也是通用的函数如下:

    function DoubleQuoteStr(S: string): string;
    begin
      if (S = '') or (S = '"')
        then S := '""'
        else begin
          if S[1] <> '"' then S := '"' + S;
          if S[System.Length(S)] <> '"' then S := S + '"';
        end;
      Result := S;
    end;
    

    遗憾的是,我仍然不确定第一个 ShellExecute HWND 参数,更具体地说,如果我的通用方法是正确的,请随时纠正我!


检测 64 位系统

其次,我们需要一个能够检测 64 位系统的函数,更具体地说,如果可执行文件在 WOW64 下运行。

function IsWow64Process: Boolean;

type
  TIsWow64Process = function(AHandle: DWORD; var AIsWow64: BOOL): BOOL; stdcall;

var
  hIsWow64Process: TIsWow64Process;
  hKernel32: DWORD;
  IsWow64: BOOL;

begin
  Result := False;

  hKernel32 := Winapi.Windows.LoadLibrary('kernel32.dll');
  if hKernel32 = 0 then Exit;

  try
    @hIsWow64Process := Winapi.Windows.GetProcAddress(hKernel32, 'IsWow64Process');
    if not System.Assigned(hIsWow64Process) then Exit;

    IsWow64 := False;
    if hIsWow64Process(Winapi.Windows.GetCurrentProcess, IsWow64) then
      Result := IsWow64;

  finally
    Winapi.Windows.FreeLibrary(hKernel32);
  end;
end;

注意事项:

    如您所见,函数从中加载库kernel32.dll 和函数IsWow64Process

    每个安全措施都应该到位,以便返回正确的结果。


运行程序

最后,我们需要调整我们的dpr文件。

在变量部分,添加:

var
$IFNDEF WIN64
  App64: string;
$ENDIF

将您的主要begin - end 部分包含在另一个begin - end 中。

并在开头添加这样的内容:

$IFNDEF WIN64

  App64 := System.SysUtils.ChangeFileExt(Application.ExeName, '64.exe');

  if not (ProcessFunctions.IsWow64Process and System.SysUtils.FileExists(App64) and
          ProcessFunctions.LaunchExecutableFile(App64, '', SW_SHOWNORMAL)) then

$ENDIF

【讨论】:

最好使用 CreateProcess 来启动另一个进程。 ShellExecute 只是其上的另一层。为什么要通过 SW_NORMAL?如果 32 位进程在其启动信息中传递了不同的标志怎么办。你不应该通过那个kb吗?使用 CreateProcess 轻松干净地完成。当然,您可以通过这种方式获得所有启动信息。您也忽略了传递任何命令行参数。使用 CreateProcess 可以让您不必担心引用字符串。而且由于 kernel32 保证在所有进程中都被加载,因此您最好使用 GetModuleHandle。 @DavidHeffernan 非常感谢您提供有用的信息。我将尝试立即研究 CreateProcess 函数。至于其他的,让我想想。

以上是关于Delphi:如何确定应用程序是不是在 Win32 / Win64 下运行并在 64 位上自动启动 64 位版本?的主要内容,如果未能解决你的问题,请参考以下文章

64位win7安装了64位的Oracle11g,用delphi7配置BDE出现下面的问题。

在 Win32 Delphi 应用程序中存储用户首选项和设置的最佳实践是啥?

如何解决“%1 不是有效的 Win32 应用程序”?

delphi7 如何安装DevExpress

在ubuntu 上使用wine 运行Delphi win32 应用

Python脚本和一个win32程序之间的通信(用Delphi开发)