MS-AccessやExcelを使っているとバージョン情報が必要になるときがあります。
(これら以外でも必要なときはありますが。) Access VBAの場合は
Application.SysCmd(acSysCmdAccessVer)
ExcelやWordの場合は
Application.Version()
で取得できますが、これはメジャー・マイナーバージョンだけでリビジョンなどは取得できません。
また、バージョン情報はエクスプローラなどから右クリック→プロパティで表示される以下のような
情報もあり最後はWinAPIでこれらの情報を取得することになります。(下図はメモ帳のバージョン
情報)
使用APIはGetFileVersionInfoSize, GetFileVersionInfo, VerQueryValueの各関数です。 まず、VC++ 2005での実装です。
Visual Studioのツールバーから任意のプロジェクト名で新規の Win32コンソールアプリケーションプロジェクトを作成してください。 Win32アプリケーションウィザードで
追加のオプション : プリコンパイル済みヘッダーにチェック 共通ファイルヘッダーを追加 : 何もチェックしない
以上を設定してください。
プロジェクト名.cppに _tmain関数ができているかと思いますが、 とりあえずこの実装ファイルはなにもしません。
次に、あとで使いまわすことをできるようにするためにバージョン情報
リソースを扱うクラスを作成してそこで情報取得の実装をします。
Visual Studioのツールバーのプロジェクト→クラスの追加でC++クラスを追加して
ください。クラス名はCFileVersionInfoとしてください。
(FileVersionInfo.hとFileVersionInfo.cppの2つのファイルができています。)
クラスの仕様はCFileVersionInfoのコンストラクタ、もしくはSetFileName メンバ関数でバージョン情報を取得するファイルを指定して、GetInfoメンバ関数で取得したい バージョン情報の種類を引数にして文字列を得ます。指定できる予め定義されている情報の種類は MSDNのVerQueryValue関数を参照してください。
CFileVersionInfoクラスのスケルトンができていますが、FileVersionInfo.h ヘッダファイルを以下のように記述してください。
//FileVersionInfo.h////////////////////////////////// //ヘッダファイル//////// #pragma once #ifndef _WINDOWS_ #include <windows.h> #endif //_WINDOWS_ enum VERINFO_TYPE //バージョン情報種類 { COMMENTS, INTERNALNAME, PRODUCTNAME, COMPANYNAME, LEGALCOPYRIGHT, PRODUCTVERSION, FILEDESCRIPTION, LEGALTRADEMARKS, PRIVATEBUILD, FILEVERSION, ORIGINALFILENAME, SPECIALBUILD }; class CFileVersionInfo { protected: LPTSTR m_pFileName; // 対象ファイル名(exe, dll) LPTSTR m_pResult; // バージョン情報格納用 private: CFileVersionInfo(const CFileVersionInfo &obj); //コピーコンストラクタ(private) public: CFileVersionInfo(); //デフォルトコンストラクタ CFileVersionInfo(LPCTSTR pFileName); //対象ファイル名を引数にしたコンストラクタ ~CFileVersionInfo(); //デストラクタ BOOL SetFileName(LPCTSTR pFileName); //ファイル名セット LPCTSTR GetInfo(VERINFO_TYPE type); //バージョン情報取得 }; |
列挙定数VERINFO_TYPEはMSDNのVerQueryValue API関数で解説されている 予め定義されているバージョン情報の種類です。これをCFileVersionInfo::GetInfoの引数に 指定して情報を取得します。CFileVersionInfoクラスの2つのメンバ変数はバージョン 情報取得対象ファイルのフルパスを保持している文字列へのポインタとGetInfoで取得した バージョン情報の文字列へのポインタです。メンバ変数がポインタなので単純にするために コピーコンストラクタのアクセス制御はprivateにして封印してます。
FileVersion.cpp実装ファイルを以下のように記述してください。
//FileVersionInfo.cpp //////////////////////////////// //実装ファイル////// #include "StdAfx.h" #include "FileVersionInfo.h" #pragma comment(lib, "Version.lib") //デフォルトコンストラクタ CFileVersionInfo::CFileVersionInfo() : m_pFileName(NULL) , m_pResult(NULL) { } //コピーコンストラクタ(privateアクセスで封印) CFileVersionInfo::CFileVersionInfo(const CFileVersionInfo &obj) : m_pFileName(NULL) , m_pResult(NULL) { } //対象ファイル名を引数にしたコンストラクタ CFileVersionInfo::CFileVersionInfo(LPCTSTR pFileName) : m_pFileName(NULL) , m_pResult(NULL) { SetFileName(pFileName); } //デストラクタ CFileVersionInfo::~CFileVersionInfo() { if (m_pFileName) delete [] m_pFileName; if (m_pResult) delete [] m_pResult; } //ファイル名セット BOOL CFileVersionInfo::SetFileName(LPCTSTR pFileName) { if (m_pFileName) delete [] m_pFileName; if (!pFileName) { m_pFileName = NULL; return TRUE; } m_pFileName = new TCHAR[lstrlen(pFileName) + 1]; if (!lstrcpy(m_pFileName, pFileName)) { delete [] m_pFileName; m_pFileName = NULL; return FALSE; } return TRUE; } //バージョン情報取得 //引数: type 取得するバージョン情報の種類 LPCTSTR CFileVersionInfo::GetInfo(VERINFO_TYPE type) { DWORD dwSize; DWORD dwZero; DWORD *pLangPage = NULL; UINT nLen; LPTSTR pTemp = NULL; //バージョン情報を保持している時は文字列解放 if (m_pResult) { delete [] m_pResult; m_pResult = NULL; } //対象ファイル名のバージョン情報のサイズ取得 dwSize = ::GetFileVersionInfoSize(m_pFileName, &dwZero); if (!dwSize) return NULL; //サイズ分のメモリ確保 BYTE *pBuf = new BYTE[dwSize]; while (TRUE) { TCHAR strSubBlock[MAX_PATH]; //バージョン情報リソース取得(pBufに取得) if (!::GetFileVersionInfo(m_pFileName, NULL, dwSize, pBuf)) break; //言語固有の情報にアクセスの為の言語識別子/コードページ取得(pLangPageに取得) if (!::VerQueryValue(pBuf, _T("\\VarFileInfo\\Translation"), (LPVOID *)&pLangPage, &nLen) || !pLangPage ) break; LPCTSTR pInfoType[] = {_T("Comments"), _T("InternalName"), _T("ProductName"), _T("CompanyName"), _T("LegalCopyRight"), _T("ProductVersion"), _T("FileDescription"), _T("LegalTrademarks"), _T("PrivateBuild"), _T("FileVersion"), _T("OriginalFilename"), _T("SpecialBuild") }; //言語固有の情報の位置特定の文字列設定(VerQueryValueの第二引数で使用) if (_stprintf_s(strSubBlock, MAX_PATH, _T("\\StringFileInfo\\%04X%04X\\%s"), LOWORD(*pLangPage), HIWORD(*pLangPage), pInfoType[type] ) == -1 ) break; //pTempに対象情報を取得 if (!::VerQueryValue(pBuf, strSubBlock, (LPVOID *)&pTemp, &nLen) || !pTemp) break; m_pResult = new TCHAR[lstrlen(pTemp) + 1]; //メンバ変数m_pResultにpTempをコピー if (!lstrcpy(m_pResult, pTemp)) { delete [] m_pResult; m_pResult = NULL; } break; } delete [] pBuf; return m_pResult; } |
実装ファイルの中心はCFileVersionInfo::GetInfoメンバ関数ですが、 引数に指定したバージョン情報の種類に対応した文字列へのポインタを返します。 返すポインタはm_pResultですが、デストラクタとCFileVersionInfo::GetInfoを 見てみればわかりますが、CFileVersionInfoオブジェクトが破壊された場合と、 再度GetInfoメンバ関数が呼ばれた場合は返却されたポインタは無効になるので 注意してください。
LPCTSTR p1, p2; CFileVersionInfo fvi(_T("C:\xxxx.exe")); p1 = fvi.GetInfo(FILEVERSION); p2 = fvi.GetInfo(PRODUCTNAME);
とした場合、p1は無効なアドレスを指しているので使用しないでください。
最後にこのプロジェクトの_tmain関数で使用してみます。
// FileVersion.cpp : コンソールアプリケーションのエントリポイントを定義します。 // #include "stdafx.h" #include "FileVersionInfo.h" int _tmain(int argc, _TCHAR* argv[]) { CFileVersionInfo fvi(argv[1]); LPCTSTR pInfoType[] = {_T("Comments"), _T("InternalName"), _T("ProductName"), _T("CompanyName"), _T("LegalCopyRight"), _T("ProductVersion"), _T("FileDescription"), _T("LegalTrademarks"), _T("PrivateBuild"), _T("FileVersion"), _T("OriginalFilename"), _T("SpecialBuild") }; for (int i=0; i<=SPECIALBUILD; i++) _tprintf(_T("%s : %s\n"), pInfoType[i], fvi.GetInfo(static_cast<VERINFO_TYPE> (i))); return 0; } |
上記を実装してビルドしてください。コンソールから下記のように バージョン情報を知りたいファイル名を引数にして実行してください。
次にVBAで実装してみます。