日曜日にGaucheのnoconsole版の作り方を教わったのでまとめてみました。
https://twitter.com/#!/anohana/status/204144556902584320
https://twitter.com/#!/SaitoAtsushi/status/204143669828268033
Gaucheのnoconsole版
Windows版のGaucheには、通常のgosh.exeに加えてgosh-noconsole.exeが含まれています。Windows環境ではGaucheのスクリプトファイルを実行する時にnoconsole版を使う事で、DOS窓(コマンドプロンプト)を表示せずにスクリプトを実行出来ます。Gaucheでnoconsole版の実行ファイルを作っているところは、配布ファイルの中のsrcディレクトリにあるMakefileに記述されています。
gosh$(EXEEXT) : $(LIBGAUCHE).$(SOEXT) $(gosh_OBJECTS) @rm -f gosh$(EXEEXT) $(LINK) $(gosh_LDFLAGS) -o gosh$(EXEEXT) $(gosh_OBJECTS) $(gosh_LDADD) $(LIBS) gosh-noconsole$(EXEEXT) : $(LIBGAUCHE).$(SOEXT) $(gosh_noconsole_OBJECTS) @rm -f gosh-noconsole$(EXEEXT) $(LINK) $(gosh_LDFLAGS) -o gosh-noconsole$(EXEEXT) $(gosh_noconsole_OBJECTS) $(gosh_LDADD) $(LIBS) -Wl,--subsystem,windows
gosh-noconsole版には、リンカのオプションに --subsystem,windows が追加されています。--subsystem,windowsのオプション付きでリンクした時は、GUIを持つWindowsアプリケーションになり、実行時にDOS窓を開きません。
Microsoft Portable Executable フォーマット
通常のWindowsアプリケーションは、Microsoft Portable Executable (PE)にしたがって作成されます。フォーマットの詳細はマイクロソフトから入手可能です。
http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
実行ファイルがDOS窓を表示するかどうかは、PEフォーマットの省略可能なヘッダーに含まれているWindows Subsystemに記録されています。現在のバージョンで許される値は、このようになっています。
Constant | Value | Description |
IMAGE_SUBSYSTEM_UNKNOWN | 0 | An unknown subsystem |
IMAGE_SUBSYSTEM_NATIVE | 1 | Device drivers and native Windows processes |
IMAGE_SUBSYSTEM_WINDOWS_GUI | 2 | The Windows graphical user interface (GUI) subsystem |
IMAGE_SUBSYSTEM_WINDOWS_CUI | 3 | The Windows character subsystem |
IMAGE_SUBSYSTEM_POSIX_CUI | 7 | The Posix character subsystem |
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI | 9 | Windows CE |
IMAGE_SUBSYSTEM_EFI_APPLICATION | 10 | An Extensible Firmware Interface (EFI) application |
IMAGE_SUBSYSTEM_EFI_BOOT_ SERVICE_DRIVER | 11 | An EFI driver with boot services |
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER | 12 | An EFI driver with run-time services |
IMAGE_SUBSYSTEM_EFI_ROM | 13 | An EFI ROM image |
IMAGE_SUBSYSTEM_XBOX | 14 | XBOX |
今回出てくるのは、この2 The Windows graphical user interface (GUI) subsystemと3 The Windows character subsystemです。
コンソールアプリケーションを作る
PEフォーマットを調べる前準備として簡単なプログラムを作りました。
コンソールにHello Worldを出力し、カレントディレクトリにHello Worldと書かれたfoo.txtを作成します。
#include <stdio.h> int main(int argc, char* argv[]) { FILE *fp; fp = fopen("foo.txt", "w"); fprintf(fp, "Hello World\n"); fclose(fp); printf("Hello World\n"); return 0; }
Visual StudioでWin32コンソールアプリケーションとしてコンパイルし、実行するとDOS窓が開いてHello Worldと表示されるはずです。これをhello-console.exeとします。
noconsole アプリケーションを作る
次は同じソースでnoconsole版を作ってみます。noconsole版を作るには、Visual Studioのリンカ設定で2カ所変更が必要です。
一つ目はリンカ→システムからサブシステムを、Windowsに変更します。このときリンカに与えられるオプションが、/SUBSYSTEM:WINDOWSで、Gaucheの--subsystem,windowsに対応します。
もう一つはリンカー→詳細設定から、エントリーポイントをmainに設定します。この設定が無いと、WinMainをエントリーポイントとしてリンクしようとします。
この2カ所を変更して、Visual Studioから実行すると、今度はDOS窓がでないでプログラムが実行されます。foo.txtを消し再度実行し、もう一度foo.txtができていればコンソール無しでプログラムが実行されていることが分かります。Visual Studioの出力ウィンドウにも「スレッド 'Win32 スレッド' (0xbbb8) はコード 0 (0x0) で終了しました。プログラム '[47764] hello.exe: ネイティブ' はコード 0 (0x0) で終了しました。」と表示され、正しく起動して終了し0を返していることが分かります。こちらは、hello-noconsole.exeにします。
PEヘッダーを覗く
早速、通常のhello-console.exeとnoconsole版のhello-noconsole.exeをバイナリエディタで覗いていましょう。左がhello-console.exe、右がhello-noconsole.exeです。
図の青枠で囲まれているところがPEシグネチャです。そこから順番に進んでいって赤枠で囲んでいるところが、Windows Subsystemです。左のhello-consoleはWindows Sybsystemが3(The Windows character subsystem)、右のhello-noconsole.exeは2(The Windows graphical user interface (GUI) subsystem)になっています。
正確にPEヘッダーの情報を得るには、dumpbin.exe を使用します。
>dumpbin /headers hello_console.exe Microsoft (R) COFF/PE Dumper Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file hello_console.exe PE signature found File Type: EXECUTABLE IMAGE 省略 400 size of headers 0 checksum 3 subsystem (Windows CUI) 8140 DLL characteristics 省略
ちなみに、コンソール版のhello-console.exeのsubsystemを、バイナリエディタで3から2に書き換えると、DOS窓を開かずにプログラムが実行できます。
参考文献
Linkers & Loaders
Linkerや実行ファイルフォーマットに関して、最初の一歩を調べるにはとても役に立つ本です。今回もPEフォーマットを調べるのに役に立ちました。流石に情報は古くなっているので、この本を参考に公式のドキュメントを読むのが良いです。