1

我需要使用 TSream 从文件(exe 或 dll)中读取 VersionInfo。

我无法使用 Windows API GetFileVersionInfo,因为我的文件位于内存 (TMemoryStream) 中,并且我不想将文件写入磁盘以获取此信息,我有一些性能限制。

有人可以帮助我吗?

4

2 回答 2

3

如果原始文件数据在内存中,则 Win32 API 无法帮助您定位文件的版本资源。您将不得不手动读取和解释文件的 PE 标头以定位文件的资源表,然后在表中循环查找所需的版本资源。找到它后,您可以使用 Win32 APIVerQueryValue()函数访问资源中的部分(但不是全部)值。我说一些是因为VerQueryValue()内部依赖于GetFileVersionInfo()在运行时建立的查找。但是,例如,访问该VS_FIXEDFILEINFO结构无需先调用就可以正常工作GetFileVersionInfo()

于 2013-05-07T18:53:57.847 回答
0

可以使用以下技术对其进行存档。

使用已加载到.EXE内存空间中的模块的 HInstance 值来RT_VERSION使用 TResourceStream 获取资源。

例如,要获取 MainModule hInstace 和相应的版本:

var module: HMODULE;
    version: String;
...
module := GetModuleHandle(nil);
version := FileVersion(base);

如果您无法像我在下面所做的那样从内存中加载资源,您可以.EXE使用PE HEADERS解析并使用 TMemoryStream 查找RT_VERSION资源。

unit Version;

interface

implementation

uses
  Winapi.Windows, System.SysUtils, System.Classes, Math;

function FileVersion(Module: HINST = 0): String;
var
  verblock:PVSFIXEDFILEINFO;
  versionMS,versionLS:cardinal;
  verlen:cardinal;
  rs:TResourceStream;
  m:TMemoryStream;
  p:pointer;
  s:cardinal;
begin
  m:=TMemoryStream.Create;
  try
    if Module = 0 then
      Module := HInstance;

    rs:=TResourceStream.CreateFromID(Module,1,RT_VERSION);
    try
      m.CopyFrom(rs,rs.Size);
    finally
      rs.Free;
    end;
    m.Position:=0;
    if VerQueryValue(m.Memory,'\',pointer(verblock),verlen) then
      begin
        VersionMS:=verblock.dwFileVersionMS;
        VersionLS:=verblock.dwFileVersionLS;
        Result:=
          IntToStr(versionMS shr 16)+'.'+
          IntToStr(versionMS and $FFFF)+'.'+
          IntToStr(VersionLS shr 16)+'.'+
          IntToStr(VersionLS and $FFFF);
      end;
    if VerQueryValue(m.Memory,PChar('\\StringFileInfo\\'+
      IntToHex(GetThreadLocale,4)+IntToHex(GetACP,4)+'\\FileDescription'),p,s) or
        VerQueryValue(m.Memory,'\\StringFileInfo\\040904E4\\FileDescription',p,s) then //en-us
          Result:=PChar(p)+' '+Result;
  finally
    m.Free;
  end;
end;

end.
于 2020-04-29T00:24:29.490 回答