Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年06月12日] [更新 2014年06月12日] Delphi サンプルプログラム集
   
318_アプリケーションのバージョン情報
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
318_AppVersionInfo.zip [4,398 KB] 2014年06月12日版 (EXE 同梱)







アプリケーションのバージョン情報


Microsoft Word 2010 の実行ファイル名は,WINWORD.EXE です.下図は,そのファイルのプロパティのダイアログを表示したところです.
この中に,[ファイルバージョン] と [製品バージョン] というのがあります.これらの値は,

  • メジャーバージョン
  • マイナーバージョン
  • ビルドバージョン
  • リビジョンバージョン

の値をドット ( . ) で連結したものとなっています.これらを含めて,このダイアログで表示される値を,アプリケーションのバージョン情報と言うことがあります.バージョン情報はまた,ファイルのプロパティの一部とも言えます.

本ページのサンプルは,主に,ファイルバージョンの値を取得する例です.いくつかの方法が考えられますので,テストしてみました.また,これらの値は,アプリケーション開発の際,リソースとして埋め込まれた情報です.そこで,これらの値を設定するテストもしてみました.



図1
バージョン情報
  • ファイルのプロパティのダイアログの [詳細] タブにある




01_SysUtils.GetFileVersion によるファイルバージョンの取得


SysUtils 内の関数 GetFileVersion を使用したファイルバージョンの取得例です.
この関数は,内部で,後述の GetFileVersionInfo 関数を使用しています.実際に取得する値は 32 ビットの符号なし整数値です.
この値の上位 16 ビットがメジャーバージョンの値,下位 16 ビットがマイナーバージョンの値となっています.この関数では,これらの値しか取得できません.サンプルでは,取得した数値をそのまま表示する場合と,メジャーバージョンとマイナーバージョンを計算する場合を比較しています.



図2
取得した値をそのまま表示
図3
バージョン情報
  • メジャーとマイナーバージョンに分けて表示

リスト1
SysUtils 内の GetFileVersion 関数を使用したバージョン情報の取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  SysUtils.GetFileVersion によるファイルバージョンの取得
//  GetFileVersionの値をそのまま表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
  aVersion     : Cardinal;
  StrText      : String;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    //ファイルのバージョンを取得
    aVersion := SysUtils.GetFileVersion(FileFullPath);

    StrText := ExtractFileName(FileFullPath) + sLineBreak + IntToStr(aVersion);
    MessageBox(Handle, PChar(StrText), '情報', MB_ICONINFORMATION);
  end;
end;

//=============================================================================
//  SysUtils.GetFileVersion によるファイルバージョンの取得
//  GetFileVersionの値からメジャーとマイナーバージョンの値を計算
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  FileFullPath : String;
  aVersion     : Cardinal;
  MajorVersion : Cardinal;
  MinorVersion : Cardinal;
  StrText      : String;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    //ファイルのバージョンを取得
    aVersion     := SysUtils.GetFileVersion(FileFullPath);
    //メジャーとマイナーバージョンの値を計算
    MajorVersion := aVersion shr 16;
    MinorVersion := aVersion and $FFFF;

    StrText := ExtractFileName(FileFullPath) + sLineBreak
             + Format('%d.%d', [MajorVersion, MinorVersion]);
    MessageBox(Handle, PChar(StrText), '情報', MB_ICONINFORMATION);
  end;
end;

end.




02_IFileSystem の GetFileVersion によるファイルバージョンの取得


IFileSystem インターフェイスを使用したファイルバージョンの取得サンプルです.
このインターフェイスは,Delphi XE には実装されていません.下記のリンクの記事のように,タイプライブラリの取り込みで,ユニット Scripting_TLB.pas を作成して利用します.

このインターフェイスの GetFileVersion メソッドを使用します.メジャー,マイナー,ビルド,リビジョンの全ての値が取得できます.



図4
IFileSystem インタフェイスを使用したバージョンの情報

リスト2
FileSystem の GetFileVersion メソッドを使用したファイルバージョンの取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Scripting_TLB;

{$R *.dfm}

//=============================================================================
//  IFileSystem の GetFileVersion によるファイルバージョンの取得
//  タイプライブラリの取り込みで作成したScripting_TLB.pasを利用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath   : String;
  objFileSystem3 : Scripting_TLB.IFileSystem3;
  strVersion     : String;
  StrText        : String;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    //GetFileVerstionというメソッドがIFileSystem3にあるのでそのオブジェクトを生成
    objFileSystem3 := CoFileSystemObject.Create;
    try
      //ファイルのバージョン文字列を取得して表示
      strVersion := objFileSystem3.GetFileVersion(FileFullPath);

      StrText := ExtractFileName(FileFullPath) + sLineBreak + strVersion;
      MessageBox(Handle, PChar(StrText), '情報', MB_ICONINFORMATION);
    finally
      objFileSystem3 := nil;
    end;
  end;
end;

end.




03_TShell を使用したバージョン情報の取得


当サイトのサンプルでよく使用している TShell オブジェクトを使用したバージョン情報の取得のサンプルです.
これも COM インターフェイスです.Delpi XE には実装されていないので,上の IFileSystem と同じく,タイプライブラリの取り込みで作成したユニットを利用します.タイプライブラリの取り込みについては,下記のリンクの記事を参考にしてください.

TShell オブジェクトを使用すると,[ファイルバージョン] [製品バージョン] だけでなく,他のバージョン情報も取得できます.このサンプルは,Windows XP では動作しません.



図5
バージョン情報の例

下図は,この後の方のサンプルの EXE ファイルの情報を取得して表示したものです.



図6
バージョン情報の例
  • 本ページの他のサンプルの EXE ファイルの例

リスト3
TShell オブジェクトを使用したファイル情報の取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Shell32_TLB, Generics.Collections;

{$R *.dfm}

//=============================================================================
//  TShell を使用したバージョン情報の取得
//  Windows XPでは動作不可
//
//  ジェネリックスが使用できないDelphiではTList,整数配列等を使用する
//  for in do が使用できないDelphiではfor to do を使用しする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
  SHShell      : Shell32_TLB.TShell;
  SHFolder     : Shell32_TLB.Folder;
  SHFolderItem : Shell32_TLB.FolderItem;
  IntList      : TList<Integer>;
  ItemNo       : Integer;
  strItem      : String;
  StrText      : String;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    SHShell      := TShell.Create(Self);
    try
      //フォルダオブジェクトを取得
      //そのフォルダ内のファイルをフォルダオブジェクトのItemとして取得
      SHFolder     := SHShell.NameSpace(ExtractFileDir(FileFullPath));
      SHFolderItem := SHFolder.ParseName(ExtractFileName(FileFullPath));

      Memo1.Lines.Clear;

      IntList := TList<Integer>.Create;
      try
        //整数型配列に取得する項目の値を格納
        IntList.Add(0);    //名前
        IntList.Add(25);   //著作連
        IntList.Add(156);  //ファイルバージョン
        IntList.Add(270);  //製品名
        IntList.Add(271);  //製品バージョン

        //格納した項目値から実際の項目名と値を取り出して表示
        for Itemno in IntList do begin
          strItem := SHFolder.GetDetailsOf(varNull, ItemNo);
          StrText := SHFolder.GetDetailsOf(SHFolderItem, ItemNo);
          Memo1.Lines.Add(strItem + '  : ' + StrText);
        end;
      finally
        IntList.Free;
      end;
    finally
      SHShell.Free;
    end;
  end;
end;

end.




04_GetFileVersionInfo 関数を使用したバージョン情報の取得


Delphi の場合,ファイルバージョン等のバージョン情報は,下図のように,設計時に [プロジェクト] [オプション] のダイアログで設定します.この設定がないとファイルバージョン等の値は取得できません.

本サンプルは,設計時に設定したこれらの内容を,実行時に取得する例です.
これまでに示した Microsoft Word のように,他のアプリケーションも,このようにバージョン情報を設定してあれば取得できることになります.
ここで設定可能な値は,MSDN オンラインの String structure の記事に一覧があります.

Delphi XE2 以降では,このバージョン情報の設定関係に問題があるようです.下のリンクの記事を参考にしてください.



図7
バージョン情報の設定
  • [プロジェクト] [ オプション] ダイアログで設定する
  • 右ペインの [バージョン番号を含める] をチェックしないと編集できない

この [プロジェクト] [オプション] で設定した内容は,コンパイルすると,EXE 内にリソースとして埋め込まれます.
下図は,添付の EXE のバージョン情報関係のリソースを示しています.これらバージョン情報のリソースの値は文字列です.これらの値を取得するために,EXE 内の PE ヘッダの情報を操作して取得する必要はありません.専用の関数が用意されています.



図8
リソース内のバージョン情報関係の値

まず GetFileVersionInfoSize 関数で情報取得に必要なサイズを取得します.
次に GetFileVersionInfo 関数でバージョン情報のリソースを取得します.そのリソースを元にして, VerQueryValue 関数で,リソース内の指定したメンバーの値を求めます.

実際に各メンバーの値を求めるには,次のような,ロケール識別子と文字コードページを 16 進数文字列にした値が必要です.この文字列の後ろに,取得するメンバの名前を追加して使用します.

VerQueryValue 関数の第 2 引数を '\VarFileInfo\Translation' にして実行します.すると,第 3 引数にロケール識別子と文字コードページの情報が,ポインタとして取得できます.これは,MSDN の記事にある Var 構造体のものですが,MSDN の記事によると,Var 構造体を使用しない場合は,ファイルの言語とコードページとして解釈されるとあります.
このことを利用します.MSDN の VerQueryValue 関数のページにコード例があります.本サンプルは,これを利用しています.


\StringFileInfo\' + ロケール識別子の 16 進文字(4桁) + コードページの 16 進文字(4桁) + '\'

以下が,サンプルコードと結果の図です.
バージョン情報の各メンバーをメンバーにしたレコード型 (構造体) を定義して使用した方が便利かも知れません.

コード中に,Sample-Number というメンバーの値がありますが,これは,テスト用に,この後のサンプルで使用している値です.



図9
本サンプルの EXE の場合
図10
システムフォルダの autoplay.dll の場合

リスト4
EXE (DLL) 内の,リソースのバージョン情報を取得する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Memo1: TMemo;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
    function GetEXEDLLVersionInfo(FileName: string): Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Name   := 'Courier New';
  Memo1.Font.Size   := 10;
  Memo1.WordWrap    := False;
  Panel1.Font := Memo1.Font;

  Panel1.Caption := '';
end;

//=============================================================================
//  アプリケーションのバージョン情報の取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExtractFileDir(Application.ExeName);
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    Memo1.Lines.Clear;
    Panel1.Caption := ExtractFileName(FileFullPath);

    //ファイルのバージョン情報等を取得して表示
    GetEXEDLLVersionInfo(FileFullPath);
  end;
end;

//-----------------------------------------------------------------------------
//  実際にアプリケーションのバージョン等の値を取得して表示するメソッド
//  引数はファイルのフルパス名
//-----------------------------------------------------------------------------
function TForm1.GetEXEDLLVersionInfo(FileName: string): Boolean;
type
  TLangAndCodePage = record
    wLanguage : WORD;
    wCodePage : WORD;
  end;
  PLangAndCodePage = ^TLangAndCodePage;

var
  dwHandle    : Cardinal;
  pInfo       : Pointer;
  pLangCode   : PLangAndCodePage;
  SubBlock    : String;
  InfoSize    : DWORD;
  pFileInfo   : Pointer;
  strList     : TStringList;
  KeyName     : String;
  StrText     : String;
begin
  Result := False;

  InfoSize := GetFileVersionInfoSize(PChar(FileName), dwHandle);
  if InfoSize = 0 then exit;

  GetMem(pInfo, InfoSize);
  try
    GetFileVersionInfo(PChar(FileName), 0, InfoSize, pInfo);

    //ロケール識別子とコードページを取得
    VerQueryValue(pInfo, '\VarFileInfo\Translation', Pointer(pLangCode), InfoSize);

    //上で取得した値を元に,
    //各種情報取得用に,VerQueryValue関数の第2引数で使用する文字列を作成
    SubBlock := IntToHex(pLangCode.wLanguage, 4) + IntToHex(pLangCode.wCodePage, 4);
    SubBlock := '\StringFileInfo\' + SubBlock + PathDelim;


    strList := TStringList.Create;
    try
      //取得する項目の名前を文字列配列に格納
      strList.Add(SubBlock + 'CompanyName');
      strList.Add(SubBlock + 'FileDescription');
      strList.Add(SubBlock + 'FileVersion');
      strList.Add(SubBlock + 'InternalName');
      strList.Add(SubBlock + 'LegalCopyright');
      strList.Add(SubBlock + 'LegalTrademarks');
      strList.Add(SubBlock + 'OriginalFilename');
      strList.Add(SubBlock + 'ProductName');
      strList.Add(SubBlock + 'ProductVersion');
      strList.Add(SubBlock + 'Comments');
      strList.Add(SubBlock + 'SpecialBuild');
      strList.Add(SubBlock + 'PrivateBuild');
      strList.Add(SubBlock + 'Sample-Number');


      //項目名に相当するメンバーの値を順番に取得
      for KeyName in strList do begin
        if VerQueryValue(pInfo,
                         PChar(KeyName),
                         Pointer(pFileInfo),
                         InfoSize) then begin

          StrText := Format('%-16s', [ExtractFileName(KeyName)]) + ' : ';
          Memo1.Lines.Add(StrText  + PChar(pFileInfo));
        end;
      end;
    finally
      strList.Free;
    end;

    Result := True;
  finally
    FreeMem(pInfo, InfoSize);
  end;
end;

end.


上のコードで,ロケール識別子を取得していますが,現在のユーザの,現在のロケール識別子であれば,次のように,GetUserDefaultLCID 関数で取得することができます.



図11
実行例
  • ロケール識別子の取得

procedure TForm1.Button1Click(Sender: TObject);
var
  Value   : Cardinal;
  StrText : String;
begin
  //ロケール識別子の取得
  Value := GetUserDefaultLCID;

  StrText := '$' + IntToStr(Value) + ' ( ' + IntToHex(Value, 4) + ' )';
  MessageBox(Handle, PChar(StrText), '情報', MB_ICONINFORMATION);
end;


また,システムで現在有効になっているコードページの識別子は,GetACP 関数 や GetOEMCP 関数で取得できます.



図12
実行例
  • コードページの取得

procedure TForm1.Button1Click(Sender: TObject);
var
  Value   : Cardinal;
  StrText : String;
begin
  //コードページの取得
  Value := GetACP;

  StrText := IntToStr(Value) + ' ( $' + IntToHex(Value, 4) + ' )';
  MessageBox(Handle, PChar(StrText), '情報', MB_ICONINFORMATION);

  //コードページの取得
  Value := GetOEMCP;

  StrText := IntToStr(Value) + ' ( $' + IntToHex(Value, 4) + ' )';
  MessageBox(Handle, PChar(StrText), '情報', MB_ICONINFORMATION);
end;




05_GetFileVersionInfo 関数を使用してファイルバージョンだけを取得


上のサンプルでは,著作権等のバージョン情報も取得していますが,ファイルバージョンだけであれば以下のコードで取得できます.
ファイルバージョンだけの取得サンプルは,本ページの最初の方にもありますが,それらは,タイプライブラリの取り込みでユニットを作成し,インターフェイスを利用するものでした.本サンプルはそのような作業は必要ありません.

使用している関数類は上のサンプルと同じですが,VerQueryValue 関数で,VS_FIXEDFILEINFO 構造体の値を取得します.この構造体のメンバーの

  • dwFileVersionMS にメジャーバージョンとマイナーバージョン
  • dwFileVersionLS にビルドバージョンとリビジョンバージョン

の値が格納されています.下のコードは,この値から,上位 16 ビットの値と下位 16 ビットの値を取り出し,書式を整えた文字列を結果として返します.



リスト5
ファイルバージョンを取得する関数
//-----------------------------------------------------------------------------
//  [ファイルのバージョン]の文字列を取得する関数
//  [ファイルのバージョン]は,ファイルのプロパティダイアログで表示される値
//  引数はファイルのフルパス名
//-----------------------------------------------------------------------------
function GetFileVersionString(FileName: string): String;
var
  dwHandle  : Cardinal;
  pInfo     : Pointer;
  InfoSize  : DWORD;
  pFileInfo : PVSFixedFileInfo;
  iVer      : array[0..3] of Cardinal;
begin
  Result := '';

  InfoSize := GetFileVersionInfoSize(PChar(FileName), dwHandle);
  if InfoSize = 0 then exit;

  GetMem(pInfo, InfoSize);
  try
    GetFileVersionInfo(PChar(FileName), 0, InfoSize, pInfo);
    VerQueryValue(pInfo, PathDelim, Pointer(pFileInfo), InfoSize);

    iVer[0] := pFileInfo.dwFileVersionMS shr 16;
    iVer[1] := pFileInfo.dwFileVersionMS and $FFFF;
    iVer[2] := pFileInfo.dwFileVersionLS shr 16;
    iVer[3] := pFileInfo.dwFileVersionLS and $FFFF;
    Result := Format('%d.%d.%d.%d', [iVer[0], iVer[1], iVer[2], iVer[3]]);
  finally
    FreeMem(pInfo, InfoSize);
  end;
end;




06_リソースファイルを使用したバージョン情報の設定


バージョン情報はリソースとして実行形式ファイルに埋め込まれます.したがって,リソースファイルを作成して設定することも可能です.
  • 新規にテキストファイルを作成する (IDE からも作成可能)
  • リソーススクリプトでリソースの内容を記述する
  • 拡張子を .rc にして保存する
  • [プロジェクト] [プロジェクトに追加] でプロジェクトに追加する
  • プロジェクトのオプションで,[バージョン番号を含める] のチェックを外す

という手順になります.テキストファイルは,IDE の [ファイル] [新規作成] のダイアログから [その他ファイル] で [テキストファイル] を選択しても作成できます.IDE から作成する時は,拡張子の指定ができ,プロジェクトへの追加も行うことができます.
今回は,スクリプトを以下のようにして,VersionTest.rc という名前で保存しました.スクリプトの内容については,MSDN の記事やネットで調べてください.

Delphi XE2 以降では [バージョン番号を含める] のチェックを外しても,そのプロジェクトを再度起動するとチェックが入った状態になるようです.この場合,Version のリソースが 2 つ作成されてしまいます.筆者の Delphi 5, 6 で確認したところでは,今回作成したリソースが,バージョン情報として取得されました.
おそらく,日本語環境では,日本語のリソースが有効になるのではないかと思われますが,詳細は不明です.

現実問題として,リソースファイルでバージョン情報を設定する必要性はないと思われますが,このようなことも可能であるというテストです.



リスト6
今回テストに使用したリソースファイルのスクリプトの内容
今回は,VersionTest.rc という名前で保存した
標準の項目以外に Sample-Number という項目を追加している
#define VER_FILEVERSION          1,2,3,456
#define VER_FILEVERSION_STR     "1.2.3.456\0"
#define VER_PRODUCTVERSION      VER_FILEVERSION
#define VER_PRODUCTVERSION_STR  VER_FILEVERSION_STR

//以下の5行は,リソースコンパイラでコンパイルする際に必要
//プロジェクトに追加して使用する際には不要
//その場合,この後の定数名のプリフィックスのVER_を削除する
#define VER_FF_DEBUG              0x00000001L
#define VER_VS_FFI_FILEFLAGSMASK  0x0000003FL
#define VER_VOS__WINDOWS32        0x00000004L
#define VER_VFT_APP               0x00000001L
#define VER_VFT2_UNKNOWN          0x00000000L

#ifndef DEBUG
#define VER_DEBUG  0L
#else
#define VER_DEBUG  VS_FF_DEBUG
#endif


1 VERSIONINFO

FILEVERSION        VER_FILEVERSION
PRODUCTVERSION     VER_PRODUCTVERSION
FILEFLAGSMASK      VER_VS_FFI_FILEFLAGSMASK
FILEFLAGS          VER_DEBUG
FILEOS             VER_VOS__WINDOWS32
FILETYPE           VER_VFT_APP
FILESUBTYPE        VER_VFT2_UNKNOWN
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "041103A4"
    BEGIN
      VALUE "CompanyName",      "\0"
      VALUE "FileDescription",  "File Version Resource Test\0"
      VALUE "FileVersion",      VER_FILEVERSION_STR
      VALUE "InternalName",     "Project1.exe\0"
      VALUE "LegalCopyright",   "Copyright (c) 2014 Mr.XRAY\0"
      VALUE "OriginalFilename", "Project1.exe\0"
      VALUE "ProductName",      "Resource Test App\0"
      VALUE "ProductVersion",   VER_PRODUCTVERSION_STR
      VALUE "Sample-Number",    "[318][06]\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x411, 0x3A4
  END
END


上で作成したリソースファイルをプロジェクトに追加してコンパイルすると,プロジェクトのソースコードに {$R 'VersionTest.res' 'VersionTest.rc'} という行が追加されます.
.res ファイルは,.rc ファイルをコンパイルした結果のファイルです.



リスト7
プロジェクトにリソース関係の行が追加される
program Project1;

{$R 'VersionTest.res' 'VersionTest.rc'}

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


作成した EXE のバージョン情報を確認したのが下図です.前に作成したバージョン情報の取得のサンプルを使用して確認しています.設定した内容が表示されています.
標準の項目以外に追加した Sample-Number という項目も表示されています.



図13
作成した EXE のバージョン情報

ファイルのプロパティダイアログで確認すると,言語のところが [日本語 (日本)] となっています.この値を Microsoft Word のように [ニュートラル言語] とするには,リソーススクリプト内の以下の箇所を変更します.
langID の [0x0] は指定なしで,ニュートラル言語を意味します. charsetID の [0x04B0](1200) はユニコードを意味しています.実際には,この設定は,開発するアプリケーションがどんな言語に対応しているかによって決めることになるでしょう.
本サンプルは日本語版だけのアプリです.したがって,0x411 としています.



    BLOCK "041103A4" --> BLOCK "000004B0" 

    VALUE "Translation", 0x411, 0x3A4 --> VALUE "Translation", 0x0, 1200


図14
ファイルのプロパティダイアログでの値




07_RC リソースコンパイラを使用したコンパイル  -  日本語対応


前項のサンプルでは,バージョン情報の値に日本語を使用すると,取得した値が文字化けします.
リソースファイルをユニコードで保存すると回避できるようです.実際にユニコードで保存してコンパイルすると,筆者の環境では,次のエラーが発生しました.



[BRCC32 エラー] VersionTest.rc(1): Expecting resource name or resource type name


そこで,今回は以下の方法で日本語が使用できるようにしてみました.
  • 新規にテキストファイルを作成する (IDE からも作成可能)
  • リソーススクリプトでリソースの内容を記述する
  • これを VersionTest.rc という名前で,ユニコード形式で保存する
  • このリソースファイルは,プロジェクトに追加しない
  • リソースコンパイラ RC.EXE でリソースをコンパイルする
  • プロジェクトのソースコードに {$R 'VersionTest.res'} を追加する
  • プロジェクトのオプションで,[バージョン番号を含める] のチェックを外す

IDE からテキストファイルを作成する場合は,プロジェクトに含めないようにします.リソースのスクリプトは以下のコードしました.
IDE 上から保存する時は,ポップアップメニューから [ファイルフォーマット] を選択し,[Little Endian (UCS-2)] とします.TEAD の場合は,文字コードセットを UNICODE (Little-Endian),秀丸エディタの場合は,エンコードの種類を Unicode (UTF-16) に,メモ帳では,文字コードを Unicode にして保存します.



リスト8
今回テストに使用したリソースファイルのスクリプトの内容
これをユニコード形式で,VersionTest.rc という名前で保存した
標準の項目以外に Sample-Number という項目を追加している
#define VER_FILEVERSION          1,2,3,456
#define VER_FILEVERSION_STR     "1.2.3.456\0"
#define VER_PRODUCTVERSION      VER_FILEVERSION
#define VER_PRODUCTVERSION_STR  VER_FILEVERSION_STR

//以下の5行は,リソースコンパイラでコンパイルする際に必要
//プロジェクトに追加して使用する際には不要
//その場合,この後の定数名のプリフィックスのVER_を削除する
#define VER_FF_DEBUG              0x00000001L
#define VER_VS_FFI_FILEFLAGSMASK  0x0000003FL
#define VER_VOS__WINDOWS32        0x00000004L
#define VER_VFT_APP               0x00000001L
#define VER_VFT2_UNKNOWN          0x00000000L

#ifndef DEBUG
#define VER_DEBUG  0L
#else
#define VER_DEBUG  VS_FF_DEBUG
#endif


1 VERSIONINFO

FILEVERSION        VER_FILEVERSION
PRODUCTVERSION     VER_PRODUCTVERSION
FILEFLAGSMASK      VER_VS_FFI_FILEFLAGSMASK
FILEFLAGS          VER_DEBUG
FILEOS             VER_VOS__WINDOWS32
FILETYPE           VER_VFT_APP
FILESUBTYPE        VER_VFT2_UNKNOWN
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "041103A4"
    BEGIN
      VALUE "CompanyName",      "\0"
      VALUE "FileDescription",  L"バージョン情報リソース設定テスト\0"
      VALUE "FileVersion",      VER_FILEVERSION_STR
      VALUE "InternalName",     "Project1.exe\0"
      VALUE "LegalCopyright",   "Copyright (c) 2014 Mr.XRAY\0"
      VALUE "OriginalFilename", "Project1.exe\0"
      VALUE "ProductName",      "Resource Test App\0"
      VALUE "ProductVersion",   VER_PRODUCTVERSION_STR
      VALUE "Sample-Number",    "[318][07]\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x411, 0x3A4
  END
END


リソースファイルを作成して保存したら,RC.EXE というリソースコンパイラでコンパイルします.
[Delphi 2009 および C++Builder 2009 の新機能 - RAD Studio] の [IDE の変更点] [リソースコンパイラの選択] の記事に,RC を使用する際は,明示的に #include <winresrc.h> が必要と書いてあります.そこで,インクルードファイルのオプションとして,このファイルを追加しています.
ただし,今回に限っては,この指定をしなくても問題ないようです.

下図は,サンプルプログラム集の [標準出力をリダイレクト] のサンプルを使用した例です.[標準リダイレクト] のプログラムを,本サンプルのプロジェクトフォルダにコピーして実行しています.



図15
リソースのコンパイル

コンパイルに成功すると,VersionTest.RES というファイルができます.これをリソースとして使用するために,プロジェクトのソースコードに手動で追加します.
作成した EXE のバージョン情報を確認したのが下図です.前に作成したバージョン情報の取得のサンプルを使用して確認しています.設定した内容が表示されています.

バージョン情報の内容を変更した場合,再度リソースファイルをコンパイルしなければなりません.この点は多少面倒です.



リスト9
プロジェクトのソースコードに {$R 'VersionTest.res'} を手動で追加する
program Project1;

{$R 'VersionTest.res'}  //追加

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


図16
作成した EXE のバージョン情報
図17
ツールチップで確認