バージョン情報を取得する(RTTI使ってみた)

バージョン情報を取得する処理を書いてみました。
RTTIを使う為に バージョン情報を保持するクラスを作ってます。
RTTIはフィールドというかプロパティに対するアクセスしかしてませんが、中々面白いですね。プロパティへのアクセスをループで回せるので処理をコンパクトに出来ました。

uses Classes, StrUtils, Types, Windows, SysUtils, Rtti;

type
  TVersionInfo = class
  private
    FComments: String;
    FInternalName: String;
    FProductName: String;
    FCompanyName: String;
    FLegalCopyright: String;
    FProductVersion: String;
    FFileDescription: String;
    FLegalTrademarks: String;
    FPrivateBuild: String;
    FFileVersion: String;
    FOriginalFilename: String;
    FSpecialBuild: String;

  public
    property Comments: String read FComments write FComments;
    property InternalName: String read FInternalName write FInternalName;
    property ProductName: String read FProductName write FProductName;
    property CompanyName: String read FCompanyName write FCompanyName;
    property LegalCopyright: String read FLegalCopyright write FLegalCopyright;
    property ProductVersion: String read FProductVersion write FProductVersion;
    property FileDescription: String read FFileDescription write FFileDescription;
    property LegalTrademarks: String read FLegalTrademarks write FLegalTrademarks;
    property PrivateBuild: String read FPrivateBuild write FPrivateBuild;
    property FileVersion: String read FFileVersion write FFileVersion;
    property OriginalFilename: String read FOriginalFilename write FOriginalFilename;
    property SpecialBuild: String read FSpecialBuild write FSpecialBuild;
  end;

// Name にバージョン情報を取得したいファイルのファイル名
// LangCode にロケールIDを指定する 日本語の場合は$0411
// バージョン情報の保持用クラス
function GetVersionResourceInfo(const Name: String; const LangCode: WORD;
  var VersionInfo: TVersionInfo): Boolean;

implementation

type
  PLangAndCodePage = ^TLangAndCodePage;
  TLangAndCodePage = record
    wLanguage: WORD;
    wCodePage: WORD;
  end;

function GetVersionResourceInfo(const Name: String; const LangCode: WORD;
  var VersionInfo: TVersionInfo): Boolean;
const
  CS_VERSION_RES: Array[0..11] of String = ('Comments','InternalName'
    ,'ProductName','CompanyName','LegalCopyright','ProductVersion'
    ,'FileDescription','LegalTrademarks','PrivateBuild','FileVersion:'
    ,'OriginalFilename','SpecialBuild');
var
  nSize, nRead: DWORD;
  pBuffer: PByte;
  pValue: PChar;
  pLang: Array of TLangAndCodePage;
  cbTranslate: Cardinal;
  I,J: Integer;
  SubBlock: String;

  LContext: TRttiContext;
  LClass: TRttiInstanceType;
  sVal: String;
begin
  Result := False;

  nSize := GetFileVersionInfoSize(PChar(Name), nSize);
  Language := 0;
  CodePage := 0;

  if nSize > 0 then
  begin
    pBuffer := AllocMem(nSize+1);
    try
      GetFileVersionInfo(PChar(Name), 0, nSize, pBuffer);
      VerQueryValue(pBuffer, '\VarFileInfo\Translation', Pointer(pLang), &cbTranslate);
      for I := 0 to cbTranslate div SizeOf(TLangAndCodePage) - 1 do
      begin
        if pLang[I].wLanguage = LangCode then
        begin
          LContext := TRttiContext.Create;
          LClass := LContext.GetType(TVersionInfo) as TRttiInstanceType;

          for J := Low(CS_VERSION_RES) to High(CS_VERSION_RES) do
          begin
            SubBlock := Format('StringFileInfo\%.4x%.4x\%s'
              , [pLang[I].wLanguage, pLang[I].wCodePage, CS_VERSION_RES[J]]);
            if VerQueryValue(pBuffer, PChar(SubBlock), Pointer(pValue), nRead) then
            begin
              if nRead > 0 then
              begin
                sVal := AnsiLeftStr(pValue, nRead);
                LClass.GetProperty(CS_VERSION_RES[J]).SetValue(VersionInfo, sVal);
              end;
            end;
          end;
          FreeAndNil(LClass);
          LContext.Free;

          Result := True;
      end;
    finally
      FreeMem(pBuffer, nSize);
    end;
  end;
end;