本記事は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 環境においては使えない。なお、マルチバイト環境では、これらはWinMainmainに変換される。

hInstancemain関数でどうやって取得するのか?
HINSTANCE hInstance;

hInstance = GetModuleHandle(NULL);

なお、wWinMainの引数hPrevInstanceは Win32 環境では常にNULLであることに留意すること。

lpCmdLinemain関数でどうやって取得するのか?

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

参考文献