2010年06月22日
Windows実行形式のMachineタイプを判別する方法
粗忽な性格の筆者の場合、Windows 上であるプログラムの 32 ビット版・64 ビット版のふたつを開発していると、ときどき「あれ、このバイナリはどっちだっけ??」という状態になることがあります。開発を行っているプラットフォームが 32 ビットであればクロスコンパイルした 64 ビットバイナリをそのまま実行することはできないためパッと判別できますが、64 ビット環境だとかしこい仮想機構 (WOW64: Windows 32bit On Windows 64bit) のおかげで 32ビットコードもすんなり実行できてしまうので逆にちょっと面倒です。
64 ビット環境の場合は所定のバイナリを実行してタスクマネージャで確認すれば、32 ビットプロセスならプロセスイメージ名の末尾に「*32」の符丁が表示されるためそれで見分けることもできますが、わざわざそのためにプロセスを起動するというのも何だか妙な話だし、また、バイナリが DLL の場合にはその方法は使えません。好ましいのは「バイナリの内容をチェックして判別する」というもっとも単純なやり方だと思いました。また、そういうツールがあれば 32 ビット環境でも 64 ビット環境でも所定のバイナリを手軽に識別できるはずです。
Windows に標準でそういう機能がないかとざっと見渡したところではどうも見当たらず、ネット上にもそれらしいツールが見つからなかったため簡単なコードを書いてみることにしました。 だいぶ前にこのブログに Windows 実行形式のヘッダ構成を書いたことがあり、その中のどこかにそういう情報があった記憶がありました。あらためて探してみるとこれでした。
MSDN: IMAGE_FILE_HEADER Structure
これらの情報をもとに C 言語で短い Windows プログラムを書きました。 引数で渡されたファイルを読んで実行形式であれば Machine タイプを表示する内容です。 ビルドずみのバイナリ (x86 & x64)
筆者は SendTo フォルダにショートカットをコピーし右クリックメニューから呼び出して使っています。
たとえばこのプログラムでこのプログラム自身の x86, x64 バイナリをチェックすると図のように表示されます。
(tanabe)
64 ビット環境の場合は所定のバイナリを実行してタスクマネージャで確認すれば、32 ビットプロセスならプロセスイメージ名の末尾に「*32」の符丁が表示されるためそれで見分けることもできますが、わざわざそのためにプロセスを起動するというのも何だか妙な話だし、また、バイナリが DLL の場合にはその方法は使えません。好ましいのは「バイナリの内容をチェックして判別する」というもっとも単純なやり方だと思いました。また、そういうツールがあれば 32 ビット環境でも 64 ビット環境でも所定のバイナリを手軽に識別できるはずです。
Windows に標準でそういう機能がないかとざっと見渡したところではどうも見当たらず、ネット上にもそれらしいツールが見つからなかったため簡単なコードを書いてみることにしました。 だいぶ前にこのブログに Windows 実行形式のヘッダ構成を書いたことがあり、その中のどこかにそういう情報があった記憶がありました。あらためて探してみるとこれでした。
MSDN: IMAGE_FILE_HEADER Structure
|Members | Machine | IMAGE_FILE_MACHINE_I386 (0x014c) x86 | IMAGE_FILE_MACHINE_IA64 (0x0200) Intel IPF | IMAGE_FILE_MACHINE_AMD64 (0x8664) x64そのまんまですね^^;
これらの情報をもとに C 言語で短い Windows プログラムを書きました。 引数で渡されたファイルを読んで実行形式であれば Machine タイプを表示する内容です。 ビルドずみのバイナリ (x86 & x64)
筆者は SendTo フォルダにショートカットをコピーし右クリックメニューから呼び出して使っています。
// // 引数で渡された Windows 実行形式ファイルの Machine タイプを判別する // #if !defined(UNICODE) #define UNICODE #define _UNICODE #endif #include <windows.h> #include <stdio.h> #include <sys/stat.h> #pragma comment(lib, "user32.lib") // IMAGE_FILE_HEADER から Machine タイプを取得 int MyCheckMachineType(LPCWSTR pszFileName) { FILE *fp; struct _stat st; IMAGE_DOS_HEADER idh; IMAGE_NT_HEADERS inh; if (_wstat(pszFileName, &st) < 0 || _wfopen_s(&fp, pszFileName, L"rb") != 0) { return -1; } if (fread(&idh, sizeof(IMAGE_DOS_HEADER), 1, fp) < 1 || idh.e_magic != IMAGE_DOS_SIGNATURE || // "MZ" idh.e_lfanew <= 0 || idh.e_lfanew >= st.st_size) { fclose(fp); return -2; } if (fseek(fp, idh.e_lfanew, SEEK_SET) != 0 || fread(&inh, sizeof(IMAGE_NT_HEADERS), 1, fp) < 1 || inh.Signature != IMAGE_NT_SIGNATURE) { // "PE\0\0" fclose(fp); return -3; } fclose(fp); switch (inh.FileHeader.Machine) { case IMAGE_FILE_MACHINE_I386: return 0; case IMAGE_FILE_MACHINE_AMD64: return 1; case IMAGE_FILE_MACHINE_IA64: return 2; } return 3; // Unknown } // 現プロセスが WOW64 下で実行中のプロセスか BOOL MyIsWow64Process() { typedef BOOL (WINAPI *DEC_ISWOW64PROCESS)(HANDLE, BOOL*); BOOL bWow64Process = FALSE; DEC_ISWOW64PROCESS pIsWow64Process = (DEC_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(L"Kernel32.dll"),"IsWow64Process"); if (!pIsWow64Process || !pIsWow64Process(GetCurrentProcess(), &bWow64Process)) { return FALSE; } return bWow64Process; } int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR lpCmdLine, int nCmdShow) { int sts; WCHAR *pName; WCHAR *m[] = { L"x86", L"x64", L"IA64", L"Unknown"}; WCHAR *e[] = { L"broken?", L"not executable", L"open error"}; #ifdef WIN32 if (MyIsWow64Process()) { MessageBox(NULL, L"WOW64 によるリダイレクトの影響を避けるために\n" \ L"Windows x64 環境では x64 版を使用して下さい", L"Warning", MB_OK); } #endif if (__argc < 2) { return 0; } sts = MyCheckMachineType(__wargv[1]); pName = wcsrchr(__wargv[1], L'\\') + 1; if (sts >= 0 && sts <= 3) { MessageBox(NULL, m[sts], pName, MB_ICONINFORMATION|MB_TOPMOST); } else { MessageBox(NULL, e[sts+3], pName, MB_TOPMOST); } return 0; }
たとえばこのプログラムでこのプログラム自身の x86, x64 バイナリをチェックすると図のように表示されます。


(tanabe)