2012-06-01 80 views
4

我怎樣才能得到我正在運行的應用程序的版本?如何獲取正在運行的可執行文件的版本?


我一直在使用GetFileVersionInfo(ParamStr(0), ...)

filename := PChar(ExtractShortPathName(ParamStr(0))); 

//Get the number of bytes he have to allocate for the file information structure 
dwInfoLength := GetFileVersionInfoSize(lptstrFilename, {var}dwHandle); 

//Get version info 
GetMem(pInfoData, dwInfoLength); 
GetFileVersionInfo(lptstrFilename, dwHandle, dwInfoLength, pInfoData); 

//Set what information we want to extract from pInfoData 
lpSubBlock := PChar(Chr(92)+Chr(0)); 

//Extract the desired data from pInfoData into the FileInformation structure 
VerQueryValue(pInfoData, lpSubBlock, PFileInformation, LengthOfReturned); 

這種技術的問題是,它需要Windows加載器load the image before the resources can be read。我用IMAGE_FILE_NET_RUN_FROM_SWAP圖像標誌(in order to avoid in-page exceptions on a fiddly network)構建我的應用程序。

這將導致Windows加載程序通過網絡再次整個圖像加載,而不是隻盯着「我」。由於我在啓動時檢查並保存了自己的版本,因此6秒的應用程序啓動變成10秒的應用程序啓動。

我該如何閱讀我的版本,我的正在運行應用程序?


我會假設Windows有沒有API來讀取一個正在運行的進程的版本,只是我從加載的文件(如果該文件不再存在,那麼就無法讀取任何版本信息)。

但我也認爲它可能會從我的進程自己的內存中讀取版本資源(當然不是管理員或調試器組的成員)。

我可以讀取我的過程的版本嗎?


相關獎勵題:如何從而不是在網絡負載PE Image資源?

回答

8

找到了,就在這裏#2:

How to determine Delphi Application Version

我已經知道如何來確定應用程序的版本,但@StijnSanders建議「精益求精」的方式,正是原因,我被擊中:

當你想知道當前正在運行的可執行文件的版本時,我強烈建議不要使用GetFileVersion!我有兩個非常充分的理由這樣做:

  • 可執行可不可訪問(斷開驅動器/股),或更改文件(.exe改名爲.bak,取而代之的是一個新的.exe文件,而不運行過程中停止)。
  • 您嘗試讀取的版本數據實際上已經加載到內存中,並且可以通過加載此資源來使用,這通常比執行額外(相對較慢)的磁盤操作要好。

我改編成:

function GetModuleVersion(Instance: THandle; out iMajor, iMinor, iRelease, iBuild: Integer): Boolean; 
var 
    fileInformation: PVSFIXEDFILEINFO; 
    verlen: Cardinal; 
    rs: TResourceStream; 
    m: TMemoryStream; 
    resource: HRSRC; 
begin 
    //You said zero, but you mean "us" 
    if Instance = 0 then 
     Instance := HInstance; 

    //UPDATE: Workaround bug in Delphi if resource doesn't exist  
    resource := FindResource(Instance, 1, RT_VERSION); 
    if (resource = 0) 
    begin 
     iMajor := 0; 
     iMinor := 0; 
     iRelease := 0; 
     iBuild := 0; 
     Result := False; 
     Exit; 
    end; 

    m := TMemoryStream.Create; 
    try 
     rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION); 
     try 
      m.CopyFrom(rs, rs.Size); 
     finally 
      rs.Free; 
     end; 

     m.Position:=0; 
     if not VerQueryValue(m.Memory, '\', /*var*/Pointer(fileInformation), /*var*/verlen) then 
     begin 
     iMajor := 0; 
      iMinor := 0; 
      iRelease := 0; 
      iBuild := 0; 
        Exit; 
     end; 

     iMajor := fileInformation.dwFileVersionMS shr 16; 
      iMinor := fileInformation.dwFileVersionMS and $FFFF; 
      iRelease := fileInformation.dwFileVersionLS shr 16; 
      iBuild := fileInformation.dwFileVersionLS and $FFFF; 
    finally 
     m.Free; 
    end; 

    Result := True; 
end; 

警告:上述代碼崩潰有時由於在Delphi一個錯誤:

rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION); 

如果沒有版本信息,德爾福試圖引發一個異常:

procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar); 
    procedure Error; 
    begin 
     raise EResNotFound.CreateFmt(SResNotFound, [Name]); 
    end; 
begin 
    HResInfo := FindResource(Instance, Name, ResType); 
    if HResInfo = 0 then Error; 
    ... 
end; 

的錯誤,當然,是PChar並不總是一個指針爲ANSI炭。使用非命名資源時,它們是整數常量,轉換爲PChar。在這種情況下:

Name: PChar = PChar(1); 

當德爾福試圖建立異常字符串,並取消引用指針0x00000001它觸發和訪問衝突。

的解決方法是手動調用FindResource(Instance, 1, RT_VERSION)第一:

var 
    ... 
    resource: HRSRC; 
begin 
    ... 
    resource := FindResource(Instance, 1, RT_VERSION); 
    if (resource = 0) 
    begin 
     iMajor := 0; 
     iMinor := 0; 
     iRelease := 0; 
     iBuild := 0; 
     Result := False; 
     Exit; 
    end; 

    m := TMemoryStream.Create; 
    ... 

注意:任何代碼發佈到公共領域。無需歸屬。

+1

事實上,這和我想的一樣:查找'VERSIONINFO'('RT_VERSION'類型)資源,然後將其提供給'VerQueryValue' API。幸運的是,它已經是Delphi代碼片段,而不是MSDN上的C++。 –

+1

那麼這不會讓你的問題完全重複? –

+0

@WarrenP答案是一樣的;即使問題不同。這個問題涉及「我自己的」過程,這不一定是有人想要的Delphi應用程序的版本可能需要。誰知道,微軟可能會添加一個API來獲取「我」的版本。 –

8

您可能要嘗試使用FindResource/LockResource API,以訪問運行模塊的VERSIONINFO資源。 MSDN文章鏈接了示例部分中的示例,並且還有一個社區評論C++ sample code正是這樣做的。這從已經加載的模塊開始,而不是從文件名(它被假設分別加載標誌指示「僅加載資源」,因此可能忽略圖像已被映射到進程中的事實)加載。

請注意,根據提供的代碼片段,您可以找到模塊的資源,然後重新使用標準的VerQueryValue API繼續從那裏進行資源分析。

+1

該示例似乎已被合併,在章節的示例中,您會發現*「有關示例,請參見[更新資源](https://msdn.microsoft.com/en-us/library/ windows */desktop/ms648008(v = vs.85).aspx#_win32_Updating_Resources)「* – Wolf

2

我建議你從JCL中讀取代碼JclFileUtils.pas。 「TJclFileVersionInfo」類有一些重載的構造函數,允許從文件和模塊句柄中獲取版本信息。

您可以使用JCL類,或至少閱讀它以檢查它是如何工作的細節。

相關問題