在运行时从可执行文件查询版本信息

时间:2016-11-15 17:01:46

标签: inno-setup

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

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

但是,这些代码示例中使用的某些数据类型不适用于Inno Setup。此外,一些示例和描述似乎表明语言和代码页本身将是一个数组,但有些示例使用它假设只有一个语言和代码页条目。 我一直试图找到语言和代码页,并根据下面的评论,我为en-us进行了硬编码。

我确实看到this回答有一个Inno Setup Pascal的代码示例,但语言和代码页计算不是基于lplpBufferCP变量让我怀疑它的正确性。

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

我根据上述解决方案编写的代码在下面列出了有问题部分的内嵌注释。

#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 个答案:

答案 0 :(得分:2)

从下面的文件版本信息的第一种语言中检索字符串的正确代码。该代码基于answer by @Jens A. KochHow to write data to an installer on the server?

该代码需要Unicode版本的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;
相关问题