バージョン情報を取得しよう

皆さんはバージョン情報を言うものをご存知でしょうか。…って、知ってますよね?
VBでプロジェクトのプロパティから設定したりそれ以外でVS_VERSION_INFOという名前のバージョンリソースを入れるとプロパティの画面から見られるようになったりします。
P_Gamma.exeのプロパティ
まあ、リソースの入れ方とかは今回は省きます。

で、こういうバージョン番号とかは、一箇所で管理していないと、ついうっかり忘れちゃってどこかで過去のバージョンが表示されちゃったりするわけですよ。私の場合6回連続でバージョン情報ダイアログの更新を忘れたことがあります。
そういうことで、こういうのを一箇所で管理してほかではそれを読み出すようにすれば、一箇所を変えれば全部変更完了であるというのはもちろん、もし忘れてしまったとしても、全部に影響が出るからどこかでわかるはずです。
今回はそのバージョンの読み出しについて説明してみます。

VB(6)でのやり方は簡単ですね。App.Major・App.Minor・App.Revisionを使えば済む話です。AppWizardでも使ってくれてますね。

で、本題のVC++6とかからやる方法です。多分VC以外でもできるような気がします。
まず何はともあれバージョンリソースを作ります。VS_VERSION_INFOという名前です。
バージョンリソース編集画面

で、これを読み込むわけです。
そのリソースはVS_VERSIONINFOという構造体に入っているようです。
どんな構造体なのか、見てみましょう。以下はMSDNライブラリより引用です。

VS_VERSIONINFO {
    WORD  wLength;
    WORD  wValueLength;
    WORD  wType;
    WCHAR szKey[];
    WORD  Padding1[];
    VS_FIXEDFILEINFO Value;
    WORD  Padding2[];
    WORD  Children[];
};

しかし、これには見ての通り可変長配列が含まれているため、Cの構造体としては定義できません。VC++でも実際用意されていません。
ところが、さらにヘルプを見ていくと、szKeyは'VS_VERSION_INFO'、つまり、終端の'\0'を含めれば"VS_VERSION_INFO\0"の16文字で固定、さらにPadding1は32ビット境界に合わせるために使われるので計算すれば2バイトで固定ということがわかり、肝心のバージョン番号が入っているValueはVS_FIXEDFILEINFOですが、winver.hで普通に定義されています。そのあとのPadding2以降はおまけみたいなものなので無視してもさしあたり問題はありません。
このことを考えると、VS_VERSIONINFOは以下のように定義しなおせそうです。

struct VS_VERSIONINFO { 
    WORD  wLength;          // この構造体のサイズ(2バイト)
    WORD  wValueLength;     // Valueのサイズ(2バイト)
    WORD  wType;            // バイナリかテキストか(2バイト)
    WCHAR szKey[16];        // "VS_VERSION_INFO\0"の16文字(32バイト)
    WORD  Padding1[1];      // 2バイト入れて合計40バイト.
    VS_FIXEDFILEINFO Value; // ここにバージョン番号が入る.
//  WORD  Padding2[];       // 可変長のためC言語の構造体では表現不可.
//  WORD  Children[];       // しかもあまり意味のないデータ.
};

さて、データの入れ物はできたので、リソースを読み込んでみます。

// フツーにリソースを読むわけですな.
HRSRC hRsrc = FindResource(NULL,(LPCTSTR)VS_VERSION_INFO,RT_VERSION));
HGLOBAL hGlobal = LoadResource(NULL,hRsrc));

そんでもってロックすればデータにアクセスできるという寸法です。

struct VS_VERSIONINFO *pData = (VS_VERSIONINFO*)LockResource(hGlobal);

そんでもって、無事アクセスできるようになったValueのVS_FIXEDFILEINFO構造体の中で、どれが必要なデータなのかというと、

typedef struct _VS_FIXEDFILEINFO {  // vsffi
    DWORD dwSignature;
    DWORD dwStrucVersion;
    DWORD dwFileVersionMS;      // ←ここ!!
    DWORD dwFileVersionLS;
    DWORD dwProductVersionMS;
    DWORD dwProductVersionLS;
    DWORD dwFileFlagsMask;
    DWORD dwFileFlags;
    DWORD dwFileOS;
    DWORD dwFileType;
    DWORD dwFileSubtype;
    DWORD dwFileDateMS;
    DWORD dwFileDateLS;
} VS_FIXEDFILEINFO;

そう、dwFileVersionMSにメジャーバージョンとマイナーバージョンがまとめて入っているわけです。
どうしてわざわざまとめておくのかが不思議ですが、そんなの私の知ったこっちゃありません。
以下のようにすれば両バージョンが取得できるということです。

major = pData->Value.dwFileVersionMS>>16;    // 左側16ビットがメジャーバージョン.
minor = pData->Value.dwFileVersionMS&0xffff; // 右側16ビットがマイナーバージョン.

VC++用サンプル