UnicodeでMingw32の落とし穴〜_tWinMain/wWinMain問題など〜
本記事はPitfall of "Unicode in Mingw32"の和訳+αです。
あまりにもmingw32での「undefined reference to `WinMain@16'」の日本からのアクセス数が多いので…
Mingw32 は完全には Unicode に対応していない。いくつかよく知られている落とし穴がある。
wWinMainが使えない
// NG:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
//...
// OK:
int main(int argc, char** argv)
{
//...
Mingw32では、wWinMain関数は使えない(→以前の記事で述べたとおり)。さらにwmain関数も同じく使えない。なので Unicode 環境においては、エントリー関数(entry function)としてmain関数を使うしかない。
加えて_tWinMainと_tmain関数もマルチバイト環境のみ使えて Unicode 環境においては使えない。なお、マルチバイト環境では、これらはWinMainとmainに変換される。
hInstanceをmain関数でどうやって取得するのか?
HINSTANCE hInstance;
hInstance = GetModuleHandle(NULL);
なお、wWinMainの引数hPrevInstanceは Win32 環境では常にNULLであることに留意すること。
lpCmdLineをmain関数でどうやって取得するのか?
GetCommandLine()を使う。
ただlpCmdLineと違って、GetCommandLine()の戻り値はコマンドライン全体であって、プログラム名も含む。
__wargvも使えないがどうするのか?
// NG:
for (i = 0; i < __argc; i++) {
arg = __wargv[i];
//...
}
// OK:
INT argc;
WCHAR **argv;
wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
for (i = 0; i < argc; i++) {
arg = wargv[i];
//...
}
LocalFree(wargv);
__wargvもMingw32では使えない。引数リストを取得するにはCommandLineToArgvW 関数とGetCommandLineW関数を使う。
なお、ここでCommandLineToArgvAという名前の関数は Win32 API には定義されていないので注意すること。
例:_tMainWin問題の私なりの解決策
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// ...
}
#ifdef _UNICODE
#ifndef _tWinMain // Mingw32 does not implement wide startup module
int main(int argc, char **argv)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
int retval = 0;
retval = _tWinMain(hInstance, NULL, _T("") /* lpCmdLine is not available*/, SW_SHOW);
return retval;
}
#endif
#endif /* _UNICODE */
なお、概述のようにlpCmdLineと完全に同じものは簡単には得られないのでlpCmdLineは使わない、という前提で使う。
引数をどうしても使う場合にはGetCommandLine()を使うことにする。