在运行时从可执行文件查询版本信息 [英] Querying Version Information from executable at runtime

查看:64
本文介绍了在运行时从可执行文件查询版本信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试查询安装程序要安装的文件的版本详细信息,并将其与正在执行的安装程序中存在的同一文件的版本详细信息进行比较.详细信息不在FileVersion或ProductVersion字段中,但可以在其他字段中,例如InternalName等.

I am trying to query the version details of a file that the installer installs and compare it against the version details of the same file present in the installer being executed. The details are not in the FileVersion or ProductVersion field but can be in other fields like InternalName etc.

我看到了用于解决此问题的Win32 API,还有一些示例代码,如:

I see Win32 APIs for solving this and also some sample code like :

  • http://delphidabbler.com/articles?article=20
  • How can i read details of file?

但是,这些代码示例中使用的某些数据类型不适用于Inno Setup.此外,一些示例和描述似乎表明语言和代码页本身将是一个数组,但是某些示例使用它时仅假设语言和代码页只有一个条目. 我被困在尝试查找语言和代码页的过程中,并根据下面的注释,对它进行了硬编码.

However, some of the data types used in those code samples do not work with Inno Setup. Further, some samples and description seem to indicate that the language and codepage itself will be an array but some samples use it assuming only one entry for language and codepage. I was stuck at trying to find the language and codepage and based on comments below, I hard coded it for en-us.

我确实看到了答案具有Inno Setup Pascal的代码示例,但语言和代码页计算不基于lplpBufferCP变量,这使我怀疑其正确性.

I do see this answer which has a code sample for Inno Setup Pascal but the language and codepage calculation not being based on the lplpBufferCP variable makes me doubt its correctness.

是否可以从Inno Setup Pascal脚本中读取通用版本信息属性?如果是这样,请提供有关如何查找语言和代码页值的帮助.

Is it possible to read generic version info properties from Inno Setup Pascal script ? If so, please help around how to find the language and code page values.

下面列出了我基于上述解决方案编写的代码,并对有问题的部分进行了在线注释.

The code I have written based on the aforesaid solutions is listed below with in-line comments for the problematic portions.

#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

function GetFileVersionInfoSize(lptstrFilename: String; lpdwHandle: Integer): Integer;
external 'GetFileVersionInfoSize{#AW}@version.dll stdcall delayload';

function GetFileVersionInfo(lptstrFilename: String; dwHandle, dwLen: Integer; var lpData: Byte): Boolean;
external 'GetFileVersionInfo{#AW}@version.dll stdcall delayload';

function VerQueryValue(var pBlock: Byte; lpSubBlock: String; var lplpBuffer: Byte; var puLen: Integer): Boolean;
external 'VerQueryValue{#AW}@version.dll stdcall delayload';

function GetFileVersionProperty(const FileName, PropertyName: String): String;
var
  VerSize: Integer;
  VerInfo: array of Byte;
  Dummy: Integer;
  InternalNameArr: array of Byte;
begin
  Result := '';
  if not FileExists(FileName) then
  begin
    Log('File ' + FileName + ' does not exist');
    Exit;
  end;

  VerSize := GetFileVersionInfoSize(FileName, 0);
  if not VerSize > 0 then
  begin
    Log('File ' + FileName + ' has no version information');
    Exit;
  end;

  SetArrayLength(VerInfo, VerSize);
  if not GetFileVersionInfo(FileName, 0, VerSize, VerInfo[0]) then
  begin
    Log('Failed to get version info for ' + FileName);
    Exit;
  end;

  if not GetFileVersionInfo(FileName, 0, VerSize, VerInfo[0]) then
  begin
    Log('Failed to get version info for ' + FileName);
    Exit;
  end;

  { Getting 'Version size = 2156' }
  Log(Format('Version size = %d', [VerSize]));

  { Hard coded value just for testing }
  SetArrayLength(InternalNameArr, 512);

  { 040904E4 hard coded for en-us }
  { Is this the correct way of querying the details ? }
  { If not, what needs to be done here }
  { TODO : InternalName hard coded. Use parameter PropertyName }
  if VerQueryValue(VerInfo[0], '\StringFileInfo\040904E4\InternalName', InternalNameArr[0], Dummy) then
  begin
    Log('Failed to query internal name of ' + FileName);
    Exit;
  end
  else
  begin
    { What needs to be done here to convert an array of byte to string ? }
    { Do I need to iterate over the array and do the conversion ?}
    { The following does not work because of SetString() being unavailable : }
    { InternalName = SetString(AnsiStr, PAnsiChar(@InternalNameArr[0]), Len);}

    { Getting 'ProductName = 0000' and 'Dummy = 0' }
    Log(Format('ProductName = %d%d', [InternalNameArr[0], InternalNameArr[1], InternalNameArr[2], InternalNameArr[3]]));
    Log(Format('Dummy = %d', [Dummy]));
  end;

{ TODO : Populate Result with appropriate value }
end;

另一种方法是将已安装文件的文件属性保存在注册表中(我对其中1个文件的1个属性感兴趣),并在安装程序中为新文件静态提供该属性.

An alternate approach could be to save the file properties of the installed file in registry (I am interested in 1 property of 1 of the files) and have the property available in the installer statically for the new file.

推荐答案

从文件版本信息的第一种语言中检索字符串的正确代码如下.该代码基于@Jens A. Koch的 answer 构建为

The correct code to retrieve a string from the first language of a file version info is below. The code builds on an answer by @Jens A. Koch to How to write data to an installer on the server?

代码需要Inno Setup的Unicode版本.

The code requires Unicode version of Inno Setup.

function GetFileVersionInfoSize(lptstrFilename: String; lpdwHandle: Integer): Integer;
  external 'GetFileVersionInfoSizeW@version.dll stdcall delayload';

function GetFileVersionInfo(
  lptstrFilename: String; dwHandle, dwLen: Integer; var lpData: Byte): Boolean;
  external 'GetFileVersionInfoW@version.dll stdcall delayload';

function VerQueryValue(
  var pBlock: Byte; lpSubBlock: String; var lplpBuffer: DWord;
  var Len: Integer): Boolean;
  external 'VerQueryValueW@version.dll stdcall delayload';

procedure RtlMoveMemoryAsString(Dest: string; Source: DWord; Len: Integer);
  external 'RtlMoveMemory@kernel32.dll stdcall';

procedure RtlMoveMemoryAsBytes(Dest: array of Byte; Source: DWord; Len: Integer);
  external 'RtlMoveMemory@kernel32.dll stdcall';

function GetFileVerInfo(FileName, VerName: String): String;
var
  Len: Integer;
  FileVerInfo: array of Byte;
  Lang: array of Byte;
  Buffer: DWord;
  LangCodepage: string;
  SubBlock: string;
begin
  Result := '';
  if FileExists(FileName) then
  begin
    Len := GetFileVersionInfoSize(FileName, 0);
    if Len > 0 then
    begin
      SetArrayLength(FileVerInfo, Len);
      if GetFileVersionInfo(FileName, 0, Len, FileVerInfo[0]) then
      begin
        if VerQueryValue(FileVerInfo[0], '\VarFileInfo\Translation', Buffer, Len) then
        begin
          if Len >= 4 then
          begin
            SetArrayLength(Lang, 4);
            RtlMoveMemoryAsBytes(Lang, Buffer, 4);
            LangCodepage :=
              Format('%.2x%.2x%.2x%.2x', [Lang[1], Lang[0], Lang[3], Lang[2]]);
            SubBlock := Format('\%s\%s\%s', ['StringFileInfo', LangCodepage, VerName]);
            if VerQueryValue(FileVerInfo[0], SubBlock, Buffer, Len) then
            begin
              SetLength(Result, Len - 1);
              RtlMoveMemoryAsString(Result, Buffer, (Len - 1) * 2);
            end;
          end;
        end;
      end;
    end;
  end;
end;

这篇关于在运行时从可执行文件查询版本信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆