5

実行中のアプリケーションのバージョンを取得するにはどうすればよいですか?


私は使用しています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 ローダーがイメージをロードする必要があることです。IMAGE_FILE_NET_RUN_FROM_SWAPイメージフラグを使用してアプリケーションをビルドします(面倒なネットワークでページ内例外を回避するため)。

これにより、Windows ローダーは、単に"me"を見るのではなく、ネットワークを介してイメージ全体を再度ロードします。起動時に自分のバージョンをチェックして保存するので、6 秒のアプリケーション起動が 10 秒のアプリケーション起動に変わります。

実行中のアプリケーションであるmeのバージョンを読み取るにはどうすればよいですか?


Windows には実行中のプロセスのバージョンを読み取るための API がなく、ロードしたファイルのみが含まれていると想定します (ファイルが存在しなくなった場合、バージョン情報を読み取ることはできません)。

しかし、プロセス自体のメモリからバージョン リソースを読み取ることも可能だと思います (もちろん、管理者またはデバッガー グループのメンバーでなくても)。

プロセスのバージョンを読み取ることはできますか?


関連ボーナスの質問:ネットワーク経由ではなく、自分PE Imageからリソースをロードするにはどうすればよいですか?

4

4 に答える 4

10

ここStackoverflowで見つけました:

Delphi アプリケーションのバージョンを確認する方法

私はすでにアプリケーションのバージョンを決定する方法を知っていましたが、@StijnSanders は、まさに私がヒットした理由から、「より良い」方法を提案しました:

現在実行中の実行可能ファイルのバージョンを知りたい場合は、GetFileVersion を使用しないことを強くお勧めします。私がこれを行うには、2 つのかなりの理由があります。

  • 実行可能ファイルにアクセスできない (ドライブ/共有が切断されている) か、変更されている (.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 then
    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);

バージョン情報がない場合、Delphi は例外を発生させようとします。

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 char へのポインタではないということです。名前のないリソースでは、それらは整数定数であり、 にキャストされPCharます。この場合:

Name: PChar = PChar(1);

Delphi が例外文字列の作成を試み、ポインタを逆参照する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;
    ...

: すべてのコードはパブリック ドメインにリリースされます。帰属は必要ありません。

于 2012-06-01T19:08:03.197 に答える
8

実行中のモジュールのリソースにアクセスするためにFindResource/ LockResourceAPIを試すことができます。VERSIONINFOMSDN の記事では、例のセクションに例がリンクされています。また、まさにこれを行うC++ サンプル コードを含むコミュニティ コメントもあります。これは、ファイル名からではなく、既にロードされているモジュールから開始します (「リソースのみをロードする」ことを示すフラグで個別にロードされると思われるため、イメージが既にプロセスにマップされているという事実を無視する可能性があります)。

提供されたコード スニペットごとに、モジュールのリソースを見つけてから、標準VerQueryValueAPI を再利用して、そこからリソースの解析を続行できることに注意してください。

于 2012-06-01T18:03:44.113 に答える
2

JCL からコード JclFileUtils.pas を読むことをお勧めします。「TJclFileVersionInfo」クラスにはオーバーロードされたコンストラクタがいくつかあり、ファイルやモジュール ハンドルからバージョン情報を取得できます。

JCL クラスを使用するか、少なくともそれを読んで、詳細にどのように機能するかを確認できます。

于 2012-06-01T19:00:55.177 に答える