mingw32での「undefined reference to `WinMain@16'」
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
$
このコンパイルオプションとき、TCHARはCHARに、_tWinMain関数はWinMain関数とみなされる。これはtchar.hでの単純な#defineで実現されている。
#define _tmain main #define _tWinMain WinMain #define _tenviron _environ #define __targv __argv
そしてこのとき、エントリーポイントWinMainから実行されることとなり、実際そのように動作する。
Unicode版wWinMainの場合
冒頭で示したUnicodeのコンパイルオプションのとき、TCHARはWCHARに、_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問題など〜に書きました。
参考文献
- mingw32-runtime 3.15.2 付属の
tchar.h - Re: [Mingw-users] link error when using _tmain