Unicode有効(-DUNICODE -D_UNICODE)時に下記のコードをmingw32-gccでビルドしようとすると"WinMain"が未定義ですという旨のリンクエラーが出てしまう。

#include <windows.h>
#include <tchar.h>

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    return 0;
}
$ gcc -o foobar.exe foobar.c -D_UNICODE -DUNICODE -mwindows -lmingw32 -lkernel32
/usr/i686-pc-mingw32/sys-root/mingw/lib/libmingw32.a(main.o):(.text+0xd2): undefined reference to `WinMain@16'
$ 

解決策はint main(int, char**)を定義するべし。これはぐぐれば比較的容易に発見可能。では、なぜか?という点の解説は日本語のページにはあまりないようだ。

ANSI版WinMainの場合

コンパイルオプションから-D_UNICODE -DUNICODEを抜くとANSI版でコンパイルされる。このとき問題なくコンパイルされ、(Unicode対応していない点を除いて)正常に動作する。

$ gcc -o foobar.exe foobar.c -mwindows -lmingw32 -lkernel32
$ 

このコンパイルオプションとき、TCHARCHARに、_tWinMain関数はWinMain関数とみなされる。これはtchar.hでの単純な#defineで実現されている。

#define _tmain      main
#define _tWinMain   WinMain
#define _tenviron  _environ
#define __targv     __argv

そしてこのとき、エントリーポイントWinMainから実行されることとなり、実際そのように動作する。

Unicode版wWinMainの場合

冒頭で示したUnicodeのコンパイルオプションのとき、TCHARWCHARに、_tWinMain関数はwWinMain関数とみなされるのが正しい動作である。同様にtchar.hを参照すると下記の通りになっている。

/*  for porting from other Windows compilers */
#if 0  /* no  wide startup module */
#define _tmain      wmain
#define _tWinMain   wWinMain
#define _tenviron   _wenviron
#define __targv     __wargv
#endif

ここにはmingw32 が「no wide startup module(Unicode向けスタートアップモジュールが未実装)」だということがかかれている。つまり、プログラムやコンパイルオプションなどは正しいが、mingw32 の未実装機能を使っているコードになっているのが正確な回答であって、簡単に言えば_tWinMainおよびwWinMainは mingw32 では現状使用できないのである。この対策としてmain関数をエントリーポイントにすることが多く行われるようである。

この面倒くさい説明を省いた結果、代替策の一つであるWinMainの代わりにmain関数の利用が解決策として提示されることになったようである。

解決策の事例はUnicodeでMingw32の落とし穴〜_tWinMain/wWinMain問題など〜に書きました。

参考文献