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()
を使うことにする。