【问题标题】:How to get version of running executable?如何获取正在运行的可执行文件的版本?
【发布时间】:2012-06-06 23:18:14
【问题描述】:

如何获取正在运行的应用程序的版本?


我一直在用GetFileVersionInfo(ParamStr(0), ...)

filename := PChar(ExtractShortPathName(ParamStr(0)));

//Get the number of bytes he have to allocate for the file information structure
dwInfoLength := GetFileVersionInfoSize(lptstrFilename, {var}dwHandle);

//Get version info
GetMem(pInfoData, dwInfoLength);
GetFileVersionInfo(lptstrFilename, dwHandle, dwInfoLength, pInfoData);

//Set what information we want to extract from pInfoData
lpSubBlock := PChar(Chr(92)+Chr(0));

//Extract the desired data from pInfoData into the FileInformation structure
VerQueryValue(pInfoData, lpSubBlock, PFileInformation, LengthOfReturned);

这种技术的问题在于它需要 Windows 加载程序到load the image before the resources can be read。我使用 IMAGE_FILE_NET_RUN_FROM_SWAP 图像标志 (in order to avoid in-page exceptions on a fiddly network) 构建我的应用程序。

这会导致 Windows 加载程序通过网络再次加载整个图像,而不是仅仅查看 “me”。由于我在启动时检查并保存了我自己的版本,因此 6 秒的应用程序启动变成了 10 秒的应用程序启动。

我如何阅读 memy 正在运行的应用程序的版本?


我会假设 Windows 没有 API 来读取正在运行的进程的版本,只有我从中加载的文件(如果文件不再存在,则它无法读取任何版本信息)。

但我也假设有可能从我的进程自己的内存中读取版本资源(当然不是管理员或调试器组的成员)。

我可以阅读我的进程的版本吗?


相关奖励问题:我如何从而不是通过网络加载PE Image资源?

【问题讨论】:

    标签: delphi winapi resources versioning delphi-5


    【解决方案1】:

    Delphi 10.3.3 提供了一种合理的方法来从运行的 exe 中读取 ProductVersion。在项目 exe 的 [Options] [ProductVersion] 中设置此值。

    设置变量为:

    var
      major, minor, build: Cardinal;
      Version: String;    
    

    设置代码为:

    // [GetProductVersion] uses 1st 3 values separated by decimal (.)
    // as: major, minor, build. 4th optional parameter is ignored.
    //
    // Gui may display -- Major, Minor, Release, Build
    // but reports as -- Major, Minor, Build
    
    if GetProductVersion(Application.ExeName, major, minor, build) then
      Version := 'v' + major.ToString() + '.' + minor.ToString()
        + ' Beta Build ' + build.ToString();
    

    【讨论】:

      【解决方案2】:

      在 Stackoverflow 上找到它:

      How to determine Delphi Application Version

      我已经知道如何确定应用程序版本,但@StijnSanders 提出了“更好”的方法,这正是我想要的原因:

      当您想知道当前运行的可执行文件的版本时,我强烈建议不要使用 GetFileVersion!我有两个很好的理由这样做:

      • 可执行文件可能无法访问(驱动器/共享断开连接),或已更改(.exe 重命名为 .bak 并替换为新的 .exe,而没有停止正在运行的进程)。
      • 您尝试读取的版本数据实际上已经加载到内存中,您可以通过加载此资源获得,这总是比执行额外(相对较慢)的磁盘操作要好。

      我改编成的:

      function GetModuleVersion(Instance: THandle; out iMajor, iMinor, iRelease, iBuild: Integer): Boolean;
      var
          fileInformation: PVSFIXEDFILEINFO;
          verlen: Cardinal;
          rs: TResourceStream;
          m: TMemoryStream;
          resource: HRSRC;
      begin
          //You said zero, but you mean "us"
          if Instance = 0 then
              Instance := HInstance;
      
          //UPDATE: Workaround bug in Delphi if resource doesn't exist    
          resource := FindResource(Instance, 1, RT_VERSION);
          if resource = 0 then
          begin
             iMajor := 0;
             iMinor := 0;
             iRelease := 0;
             iBuild := 0;
             Result := False;
             Exit;
          end;
      
          m := TMemoryStream.Create;
          try
              rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
              try
                  m.CopyFrom(rs, rs.Size);
              finally
                  rs.Free;
              end;
      
              m.Position:=0;
              if not VerQueryValue(m.Memory, '\', (*var*)Pointer(fileInformation), (*var*)verlen) then
              begin
                  iMajor := 0;
                  iMinor := 0;
                  iRelease := 0;
                  iBuild := 0;
                  Exit;
              end;
      
              iMajor := fileInformation.dwFileVersionMS shr 16;
              iMinor := fileInformation.dwFileVersionMS and $FFFF;
              iRelease := fileInformation.dwFileVersionLS shr 16;
              iBuild := fileInformation.dwFileVersionLS and $FFFF;
          finally
              m.Free;
          end;
      
          Result := True;
      end;
      

      警告:上述代码有时会因 Delphi 中的错误而崩溃:

      rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
      

      如果没有版本信息,Delphi 会尝试引发异常:

      procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar);
         procedure Error;
         begin
            raise EResNotFound.CreateFmt(SResNotFound, [Name]);
         end;
      begin
         HResInfo := FindResource(Instance, Name, ResType);
         if HResInfo = 0 then Error;
         ...
      end;
      

      当然,错误在于 PChar 并不总是 指向 ansi char 的指针。对于未命名的资源,它们是整数常量,转换为 PChar。在这种情况下:

      Name: PChar = PChar(1);
      

      当 Delphi 尝试构建异常字符串并取消引用指针 0x00000001 时,它会触发访问冲突。

      修复方法是先手动调用FindResource(Instance, 1, RT_VERSION)

      var
          ...
          resource: HRSRC;
      begin
          ...
          resource := FindResource(Instance, 1, RT_VERSION);
          if (resource = 0) 
          begin
             iMajor := 0;
             iMinor := 0;
             iRelease := 0;
             iBuild := 0;
             Result := False;
             Exit;
          end;
      
          m := TMemoryStream.Create;
          ...
      

      注意:任何代码都会发布到公共领域。无需署名。

      【讨论】:

      • 确实和我想的一样:查找VERSIONINFORT_VERSION类型)资源,然后将其提供给VerQueryValue API。幸运的是,它已经是 Delphi 代码 sn-p 而不是 MSDN 上的 C++。
      • 所以这不是让您的问题完全重复吗?
      • @WarrenP 答案是一样的;即使问题不同。这个问题涉及“我自己的”过程,这不一定是想要 Delphi 应用程序版本的人可能想要的。谁知道呢,微软可能会添加一个 API 来获取“我”的版本。
      • 因此,您在问题(我自己的)中的两个词差异对答案没有影响(不足为奇),因此,重复。我可以在问题中添加蓝色、紫色和绿色三个词,它们对答案同样没有影响,因此它们是相同的问题。
      • @Warren,阅读 2 个问题...事实上,另一个问题的可能答案之一是对这个问题的答案并不意味着问题是相同的。这个问题要具体得多,不能保证简单搜索 THIS 问题就能找到另一个问题,更不用说其他答案都不适用于这里。
      【解决方案3】:

      您可能想尝试FindResource/LockResource API 以访问正在运行的模块的VERSIONINFO 资源。 MSDN 文章在示例部分链接了一个示例,还有一个带有C++ sample code 的社区评论正是这样做的。这从已经加载的模块开始,而不是从文件名开始(据说是单独加载的,带有指示“仅加载资源”的标志,因此可能忽略了图像已经映射到进程中的事实)。

      请注意,根据提供的代码 sn-p,您可以找到模块的资源,然后重用标准 VerQueryValue API 以继续从那里解析资源。

      【讨论】:

      • 该示例似乎已被合并,在示例部分中,您会发现 "For an example, see Updating Resources."
      【解决方案4】:

      我建议您阅读 JCL 中的代码 JclFileUtils.pas。 “TJclFileVersionInfo”类有一些重载的构造函数,允许从文件和模块句柄获取版本信息。

      您可以使用 JCL 类,或者至少阅读它以详细了解它的工作原理。

      【讨论】:

      • 这根本没有帮助:两个构造函数(Window: HWNDModule: HMODULE 首先从这个句柄中检索文件名,然后调用 FileName: string 构造函数。
      猜你喜欢
      • 2012-06-29
      • 1970-01-01
      • 2019-05-19
      • 2010-09-24
      • 2014-09-07
      • 2021-03-11
      • 1970-01-01
      • 1970-01-01
      • 2020-06-02
      相关资源
      最近更新 更多