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

问题描述:

我试图查询安装程序安装的文件的版本详细信息,并将其与正在执行的安装程序中存在的同一文件的版本详细信息进行比较。细节并不在文件版本或领域的ProductVersion,但可以像INTERNALNAME等在运行时从可执行文件查询版本信息

等领域我看到的Win32 API解决这一点,也有一些示例代码,如:

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

我看到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; 

的替代方法可以保存在注册表中的安装文件的文件属性(我感兴趣的文件11个属性),并在安装程序中可用的属性中静态的新文件。

+0

那么你是否尝试了@Jens A. Koch的代码? –

+0

你究竟需要什么?这是你的可执行文件,对吗?所以你知道它使用它的资源是什么语言和代码页,不是吗? –

+0

也许您可以向我们展示可执行文件版本信息的转储,以使您的问题更加清晰。 –

下面是从文件版本信息的第一语言中检索字符串的正确代码。该代码建立在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 '[email protected] stdcall delayload'; 

function GetFileVersionInfo(
    lptstrFilename: String; dwHandle, dwLen: Integer; var lpData: Byte): Boolean; 
    external '[email protected] stdcall delayload'; 

function VerQueryValue(
    var pBlock: Byte; lpSubBlock: String; var lplpBuffer: DWord; 
    var Len: Integer): Boolean; 
    external '[email protected] stdcall delayload'; 

procedure RtlMoveMemoryAsString(Dest: string; Source: DWord; Len: Integer); 
    external '[email protected] stdcall'; 

procedure RtlMoveMemoryAsBytes(Dest: array of Byte; Source: DWord; Len: Integer); 
    external '[email protected] 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; 
+0

谢谢马丁。还有其他一些关于如何从Win32 API的OUT参数中获取数据的SO问题,但是他们都没有使用RtlMoveMemoryXXX()API进行讨论。从Format()调用(使用Lang [1],Lang [0],Lang [3],Lang [2]),它似乎是特定于字节序的。是这样吗,还是别的吗? – Anand

+0

为此目的使用'RtlMoveMemory'是我的“发明”+是的,它是关于字节序的。 –