2016-11-15 2 views
0

설치 프로그램이 설치하는 파일의 버전 정보를 쿼리하고 실행중인 설치 프로그램에있는 동일한 파일의 버전 정보와 비교하려고합니다.런타임에 실행 파일에서 버전 정보 쿼리

그러나 이러한 코드 샘플에 사용 된 일부 데이터 형식은 Inno Setup에서 작동하지 않습니다. 또한 일부 샘플 및 설명은 언어 및 코드 페이지 자체가 배열이되지만 일부 샘플에서는 언어 및 코드 페이지에 대해 하나의 항목 만 사용한다고 표시하는 것 같습니다. 언어 및 코드 페이지를 찾으려고 애썼지 만 아래 주석에 따라 en-us로 코딩했습니다.

나는 Inno Setup Pascal에 대한 코드 샘플이있는 this 대답을 볼 수 있지만 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

그래서 @Jens A. Koch가 코드를 사용해 보셨습니까? –

+0

실제로 필요한 것은 무엇입니까? 네 실행 파일 맞지? 그래서 당신은 리소스와 리소스에 어떤 언어와 코드 페이지가 사용되는지 알고 있습니다. –

+0

어쩌면 질문을 더 분명하게하기 위해 실행 파일의 버전 정보를 덤프 할 수 있습니다. –

답변

1

파일 버전 정보의 첫 번째 언어에서 문자열을 검색하는 올바른 코드는 다음과 같습니다. 코드는 answer by @Jens A. Koch에서 How to write data to an installer on the server?까지

이 코드에는 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

Martin 감사합니다. Win32 API의 OUT 매개 변수에서 데이터를 얻는 방법에 대한 다른 질문이 있지만 RtlMoveMemoryXXX() API를 사용하여 논의한 것은 없습니다. Lang [1], Lang [0], Lang [3], Lang [2]를 사용하는 Format() 호출에서 endianness에만 해당하는 것으로 보입니다. 그렇습니까, 아니면 다른 것입니까? – Anand

+0

이 목적을위한'RtlMoveMemory'의 사용은 나의 "발명품"입니다 + 예, 그것은 엔디안에 관한 것입니다. –