2016-11-15 101 views
0

我試圖查詢安裝程序安裝的文件的版本詳細信息,並將其與正在執行的安裝程序中存在的同一文件的版本詳細信息進行比較。細節並不在文件版本或領域的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

也許您可以向我們展示可執行文件版本信息的轉儲,以使您的問題更加清晰。 –

回答

1

下面是從文件版本信息的第一語言中檢索字符串的正確代碼。該代碼建立在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'是我的「發明」+是的,它是關於字節序的。 –

相關問題