ЧИТАТЬ VersionInfo из файла с помощью TFileStream

Мне нужно прочитать VersionInfo из файла (EXE или DLL), используя TSream.

Я не могу использовать Windows API GetFileVersionInfo, потому что мой файл находится в памяти (TMemoryStream), и я не хочу записывать файл на диск для получения этой информации, у меня есть некоторые ограничения производительности.

Кто-то может мне помочь?

1 ответ

Если исходные данные файла находятся в памяти, Win32 API не может помочь вам найти ресурс версии файла. Вам нужно будет вручную прочитать и интерпретировать PE-заголовок файла, чтобы найти таблицу ресурсов файла, а затем перебрать таблицу в поисках нужного ресурса версии. Как только вы нашли его, вы можете использовать Win32 API VerQueryValue() функция для доступа к некоторым (но не ко всем) значениям внутри ресурса. Я говорю некоторые, потому что VerQueryValue() внутренне опирается на поиск, который GetFileVersionInfo() устанавливает во время выполнения. Тем не менее, доступ к VS_FIXEDFILEINFO структура, например, отлично работает без вызова GetFileVersionInfo() первый.

Его можно заархивировать, используя описанную ниже технику.

Используйте значение HInstance модулей, уже загруженных в .EXE место в памяти, чтобы получить RT_VERSION ресурс с помощью TResourceStream.

Например, чтобы получить MainModule hInstace и соответствующую версию:

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

Если вы не можете загрузить как ресурс из памяти, как я сделал ниже, вы можете проанализировать .EXEиспользуя PE HEADERS и найдитеRT_VERSION ресурс с использованием TMemoryStream.

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.
Другие вопросы по тегам