gcc -nostartfiles その6

gcc の -nostartfiles オプションを有効にしてコンパイル&リンクすると、ファイルサイズが小さくなるかわりに、エントリポイントが、先頭に定義した関数にすりかわる。そこで、その関数で WinMain を呼ぶことにより、スタンダードな C ソースコードの流れをシミュレートする。

#include <windows.h>
void Main() {
  HINSTANCE hi = GetModuleHandle(NULL);
  HINSTANCE hp = 0;
  LPSTR cl = __argv;
  int exitcode = WinMain(hi, hp, cl, 0);
  ExitProcess(exitcode);
}
int WINAPI WinMain(HINSTANCE hi, HINSTANCE hp, LPSTR cl, int cs) {
  MessageBox(NULL, TEXT("I am in WinMain"), TEXT(""), MB_OK);
  return 0;
}

第一引数

第一引数には、GetModuleHandle(NULL) で取得できるインスタンスハンドルを渡す。

第二引数

第二引数にも、HINSTANCE 型の値を指定する必要があるが、この値は、16 ビットのアプリケーションでのみ使用される。現代で一般的な 32 ビットおよび 64 ビットアプリケーションでは不要であり、かつ、本来の WinMain においても、0 が入っているので、0 でよい。

第三引数

第三引数には、本来の WinMain では、コマンドライン引数の文字列が入っている。たとえば、上記の hello.c をコンパイル&リンクし、実行ファイル hello.exe を作成したとする。そして、hello.exe を hello a b c というふうに、引数をつけて起動すると、"a b c" という文字列が入る。

むずかしい方法

GetCommandLine() で "hello a b c" を得ることはできるが、そこから "a b c" だけを抜き出すことは困難である。もっとも簡単そうな方法は、CommandLineToArgvW(GetCommandLineW(), (int)cmdlen) などとして、"hello" と "a" と "b" と "c" に分解された配列を得たのち、lstrcat を用いて、添え字 1 以後すべてを半角空白でつなげて、"a b c" を組み立てるというものだが、これで得られる "a b c" は LPWSTR である。WinMain の第三引数の型は LPSTR であるため、渡すことができない。

また、ワイド文字列からマルチバイト文字列に変換する wcstombs という関数があり、これを使って、LPWSTR の "a b c" を LPSTR "a b c" に変換することができればよいのだが、非常に大がかりな処理をしなければならないだろう。

かんたんな方法

一方で、__argv というビルトイン変数を使う方法がある。__argv には、本来の WinMain 第三引数に渡る文字列と同等の文字列が入っている。gcc コンパイラからの警告を出されないために、この変数を LPSTR 型にキャストするか、LPSTR 型の変数にアドレスを代入するかして、WinMain の第三引数に渡すとよい。