gcc -nostartfiles その3

何もしないはずのソースコード hello.c を gcc -nostartfiles でコンパイルして起動すると、定義した関数が、呼び出していないのに実行された。

#include <windows.h>
void hello() {
  MessageBox(NULL, TEXT("Hello, world!"), TEXT(""), MB_OK);
}
int WINAPI WinMain(HINSTANCE hi, HINSTANCE hp, LPSTR cl, int cs) {
  return 0;
}

どうして、メッセージボックスが表示されるのだろうか。

-nostartfiles を使うと、定義してあるだけの関数が自動で実行されるという、奇妙な挙動になるのだろうか。このことについて、実験を行ってみよう。

どうなる?

コードを編集する。

  1. 関数 hello のあとに、関数 bye を定義する
  2. 関数 WinMain でメッセージボックスを表示する処理を追加する
#include <windows.h>
void hello() {
  MessageBox(NULL, TEXT("Hello, world!"), TEXT(""), MB_OK);
}
void bye() {
  MessageBox(NULL, TEXT("BYE!!"), TEXT(""), MB_OK);
}
int WINAPI WinMain(HINSTANCE hi, HINSTANCE hp, LPSTR cl, int cs) {
  MessageBox(NULL, TEXT("I am in WinMain"), TEXT(""), MB_OK);
  return 0;
}

そして、gcc -mwindows -nostartfiles hello.c -o hello.exe でコンパイル&リンク後、実行する。

こうなる

すると、以下のような振る舞いとなる。

  1. まず、改変前のコードと同じように「Hello, world!」メッセージボックスが表示される。
  2. そのメッセージボックスを閉じると、それ以上は何も表示されない。
  3. hello.exe はバックグラウンドで生き残っている。

この結果からして、-nostartfiles は、定義してある全ての関数を自動で実行するわけではなく、最初に定義された関数のみを実行するようだ。さらには、なんと、WinMain が実行されていないことも判明した。

どうして?

-nostartfiles によって、標準スタートアップファイルのリンクを行わないようにすると、どうやら、本来のエントリポイントである関数 WinMain がそうでなくなり、先頭に定義した関数 hello がエントリポイントになる。

また、メッセージボックスを閉じることで、すべての処理が終了しても、アプリケーションそのものが終了しない原因は、どうやら、プロセスを終了するための関数 ExitProcess が実行されていないからである。ExitProcess は、通常、スタートアップルーチンが行うものなのだ。