実行ファイル(DLLやEXE)のバージョンを確認する方法。
VC++でプロジェクトを作ると、リソースのひとつに「Version」(IDはVS_VERSION_INFO)というものがある。
ここで設定したFileVersionやProductVersion(「1, 0, 0, 1」という感じの値)は、
実行ファイルを右クリックしてプロパティを開くと、バージョンタブ内の「ファイルバージョン」や「製品バージョン」で確認できる。
プログラムからは、Win32APIのGetFileVersionInfoやVerQueryValueを使用して取得できる。
GetFileVersionInfoに実行ファイルのファイル名を指定することで、ファイル内に保持されたリソースを取得できる。
リソースは日本語以外の言語でも設定できるので、ファイルバージョンやプロダクトバージョン等は それぞれの言語ごとに実行ファイルに記録されている。
具体的には、言語とコードページという数値によって区別している。(日本語で作った実行ファイルは、041104b0。先頭の0411が
「日本語」(「英語(アメリカ)」だと0409)で、後尾の04b0が「UNICODE」。いずれも十六進数2バイト)
これらの情報は階層構造になっており、VerQueryValueでは階層を(ディレクトリを辿るのと同様に)指定する。[2006-09-14]
| 階層の例 | 値の例 | 内容 | |||
|---|---|---|---|---|---|
| \ | VarFileInfo | Translation | 040904b0 041104b0 |
保持している言語とコードページの一覧 | |
| StringFileInfo | 040904b0 | ProductVersion | 1, 0, 0, 1 | 製品バージョン(英語) | |
| FileVersion | 1, 0, 0, 1 | ファイルバージョン(英語) | |||
| Comment | sample | コメント(英語) | |||
| 041104b0 | ProductVersion | 1, 0, 0, 1 | 製品バージョン(日本語) | ||
| FileVersion | 1, 0, 0, 1 | ファイルバージョン(日本語) | |||
| Comment | サンプル | コメント(日本語) | |||
//dllファイルやexeファイル内のリソースのバージョン情報を表示する
BOOL PrintFileVersion(char* path)
{
#pragma comment(lib, "version.lib")
DWORD dwZero = 0;
DWORD dwVerInfoSize = GetFileVersionInfoSize(path, &dwZero);
if(dwVerInfoSize == 0) {
printError("GetFileVersionInfoSize", GetLastError());
return FALSE;
}
unsigned char *pBlock;
pBlock = new unsigned char[dwVerInfoSize];
if(pBlock == NULL) return FALSE;
GetFileVersionInfo(path, dwZero, dwVerInfoSize, pBlock);
//バージョンを取得する為のバッファ
void *pvVersion;
UINT VersionLen;
//全言語のプロダクトバージョンを表示する例
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
UINT TranslateLen;
VerQueryValue(pBlock, TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&lpTranslate, &TranslateLen);
for(UINT i=0;i<TranslateLen/sizeof(*lpTranslate);i++){
//コードページを指定
char name[256];
wsprintf(name, TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"),
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage
);
if(VerQueryValue(pBlock, name, &pvVersion, &VersionLen)){
printf("ProductVersion[%04x%04x]:%d:%s\n",
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage,
VersionLen, pvVersion
);
}
}
//言語のコードページが分かっている場合は、それを直接指定すればよい
//プロダクトバージョン
if(VerQueryValue(pBlock, TEXT("\\StringFileInfo\\041104b0\\ProductVersion"), &pvVersion, &VersionLen)){
printf("ProductVersion:%d:%s\n", VersionLen, pvVersion);
}
//ファイルバージョン
if(VerQueryValue(pBlock, TEXT("\\StringFileInfo\\041104b0\\FileVersion"), &pvVersion, &VersionLen)){
printf("FileVersion:%d:%s\n", VersionLen, pvVersion);
}
//×イメージバージョンはリソースではないので取得できない(エラーになる)
//if(VerQueryValue(pBlock, TEXT("\\StringFileInfo\\041104b0\\ImageVersion"), &pvVersion, &VersionLen)){
// printf("ImageVersion:%d:%s\n", VersionLen, pvVersion);
//}
//×StringFileInfo全体を取得することはできない(エラーにはならないが、長さは0が返ってくる)
//if(VerQueryValue(pBlock, TEXT("\\StringFileInfo"), &pvVersion, &VersionLen)){
// printf("FileInfo:%d:%s\n", VersionLen, pvVersion); //→len=0
//}
delete [] pBlock;
return TRUE;
}
DLLを作成するときによく使われるdefファイルだが、その中でVERSIONという値
(「1.0」という感じ)を設定することが出来る。
ここで設定したバージョンは、dumpbinやDependency
Walkerによって確認できる。
>dumpbin.exe /headers 実行ファイル名 | findstr /c:"image version"
プログラムから取得する方法は無いっぽい。
(DLLやEXEといった実行ファイルはPEヘッダーと呼ばれるデータを持っており、defファイルで設定したイメージバージョンはこの中に入っているらしい。で、PEヘッダーの内容を取得するAPIは無いらしい。やるなら自分で解析するしかないらしい…)
Windowsが提供しているDLLの中には、DLLGetVersionという関数を呼べばバージョンが取得できるものがあるらしい。
(つまり、そのDLLの中に DLLGetVersionという関数が定義されている、というだけのことだが)
#include <shlwapi.h>
//DLLGetVersionという関数があるDLLでは、それを使ってバージョンが取得できる
void PrintWinDllVersion(const char *path)
{
HMODULE hModule = LoadLibrary(path);
if(hModule == NULL){
printError("LoadLibrary", GetLastError());
return;
}
DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hModule, "DllGetVersion");
if(DllGetVersion == NULL){
printError("GetProcAddress(DLLGetVersion)", GetLastError());
return;
}
DLLVERSIONINFO dvi = { 0 };
dvi.cbSize = sizeof(dvi);
HRESULT hr = DllGetVersion(&dvi);
if(SUCCEEDED(hr)){
printf("%s version:%x.%x\n", path, dvi.dwMajorVersion, dvi.dwMinorVersion);
}
FreeLibrary(hModule);
}
使用例:
PrintWinDllVersion("shlwapi.dll");
PrintWinDllVersion("Shell32.dll");