2010-02-08 37 views
5

我試圖調用標準的Win32 API函數來獲取文件版本信息,使用win32-api libraryruby​​ win32api&structs(VerQueryValue)

3 version.dll函數是GetFileVersionInfoSize,GetFileVersionInfo和VerQueryValue。然後我調用kernel32.dll中的RtlMoveMemory來獲取VS_FIXEDFILEINFO結構的副本(請參閱Microsoft文檔:http://msdn.microsoft.com/en-us/library/ms646997%28VS.85%29.aspx)。

我畫了一個例子,我看到使用VB:http://support.microsoft.com/kb/139491

我的問題是,最終返回的數據似乎不符合預期的結構,事實上它甚至不會返回一致的值。我懷疑數據在某些時候會受到損壞,可能是VerQueryValue或RtlMoveMemory。

下面是代碼:

GetFileVersionInfoSize = Win32::API.new('GetFileVersionInfoSize','PP','I','version.dll') 
GetFileVersionInfo = Win32::API.new('GetFileVersionInfo','PIIP','I', 'version.dll') 
VerQueryValue = Win32::API.new('VerQueryValue','PPPP','I', 'version.dll') 
RtlMoveMemory = Win32::API.new('RtlMoveMemory', 'PPI', 'V', 'kernel32.dll') 

buf = [0].pack('L') 
version_size = GetFileVersionInfoSize.call(myfile + "\0", buf) 
raise Exception.new if version_size == 0 #TODO 

version_info = 0.chr * version_size 
version_ok = GetFileVersionInfo.call(file, 0, version_size, version_info) 
raise Exception.new if version_ok == 0 #TODO 

addr = [0].pack('L') 
size = [0].pack('L') 
query_ok = VerQueryValue.call(version_info, "\\\0", addr, size) 
raise Exception.new if query_ok == 0  #TODO 

# note that at this point, size == 4 -- is that right? 

fixed_info = Array.new(13,0).pack('L*') 
RtlMoveMemory.call(fixed_info, addr, fixed_info.length) 

# fixed_info.unpack('L*') #=> seemingly random data, usually only the first two dwords' worth and the rest 0. 
+1

我想我想通了..基本上VerQueryValue返回一個指針的指針(上面的變量地址),而RtlMoveMemory想要一個指針,即久,地址引用。 所以我改變了聲明: RtlMoveMemory =的Win32 :: API.new( 'RtlMoveMemory', 'PLI', 'V', 'KERNEL32.DLL') ,然後調用它: RtlMoveMemory.call( fixed_info,addr.unpack('L')[0],fixed_info.length) – 2010-02-08 04:55:53

回答

3

這是一個完整的代碼,我開始工作,以防其他人都在尋找這樣的功能。

返回與四個部分的產品/文件的版本號(即所謂的dll文件的屬性窗口中的「文件版本」)的數組:

def file_version ref, options = {} 
    options = {:path => LIBDIR, :extension => 'dll'}.merge(options) 
    begin 
     file = File.join(ROOT, options[:path],"#{ref}.#{options[:extension]}").gsub(/\//,"\\") 
     buf = [0].pack('L') 
     version_size = GetFileVersionInfoSize.call(file + "\0", buf) 
     raise Exception.new if version_size == 0 #TODO 

     version_info = 0.chr * version_size 
     version_ok = GetFileVersionInfo.call(file, 0, version_size, version_info) 
     raise Exception.new if version_ok == 0  #TODO 

     addr = [0].pack('L') 
     size = [0].pack('L') 
     query_ok = VerQueryValue.call(version_info, "\\\0", addr, size) 
     raise Exception.new if query_ok == 0  #TODO 

     fixed_info = Array.new(18,0).pack('LSSSSSSSSSSLLLLLLL') 
     RtlMoveMemory.call(fixed_info, addr.unpack('L')[0], fixed_info.length) 

     fixed_info.unpack('LSSSSSSSSSSLLLLLLL')[5..8].reverse 

    rescue 
     [] 
    end 
end 
1

https://stackoverflow.com/a/2224681/3730446答案並不完全正確:VS_FIXEDFILEINFO結構包含單獨的FileVersionProductVersion。該代碼返回的版本號由ProductVersion的兩個更重要的組件和FileVersion的兩個較不重要的組件組成。大多數時候我都看到了,這並不重要,因爲Product-FileVersion都具有相同的價值,但你永遠不知道你在野外會遇到什麼。

我們可以通過比較來自http://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspxVS_FIXEDFILEINFO結構和我們用來打包和解包緩衝區中的格式字符串看到這一點:

typedef struct tagVS_FIXEDFILEINFO { 
    DWORD dwSignature;  // 0: L 
    DWORD dwStrucVersion;  // 1: S 
           // 2: S 
    DWORD dwFileVersionMS; // 3: S 
           // 4: S 
    DWORD dwFileVersionLS; // 5: S 
           // 6: S 
    DWORD dwProductVersionMS; // 7: S 
           // 8: S 
    DWORD dwProductVersionLS; // 9: S 
           // 10: S 
    DWORD dwFileFlagsMask; // 11: L 
    DWORD dwFileFlags;  // 12: L 
    DWORD dwFileOS;   // 13: L 
    DWORD dwFileType;   // 14: L 
    DWORD dwFileSubtype;  // 15: L 
    DWORD dwFileDateMS;  // 16: L 
    DWORD dwFileDateLS;  // 17: L 
} VS_FIXEDFILEINFO; 

下標5至8,然後,由dwFileVersionLSdwProductVersionMS。獲取FileVersionProductVersion正確的應該是這樣的:

info = fixed_info.unpack('LSSSSSSSSSSLLLLLLL') 
file_version = [ info[4], info[3], info[6], info[5] ] 
product_version = [ info[8], info[7], info[10], info[9] ]