ホーム < ゲームつくろー! < Programming TIPs編

その20 Win32アプリケーションでコンソールウィンドウを出す


 DirectXなどWin32アプリケーションを作っている時に欲しいなぁと思ってしまうのが「デバッグウィンドウ」。キャラクタの位置とかメモリの状態など、動きながら見たい物は案外多い物です。しかし、ゲームの画面上に文字をぶわーっと出すのもちょっと芸がありません。そこで、Win32アプリケーションだけどもコンソールウィンドウを出して、そこにデバッグ情報を垂れ流してみましょう。



@ コンソールウィンドウを出す

 Win32アプリケーションからコンソールウィンドウを出すのは、盲目的にやるなら案外簡単で、以下の数行のコードを一度走らせるだけです:

#include <io.h>
#include <Fcntl.h>

int hConsole = 0;

void createConsoleWindow() {
    AllocConsole();
    hConsole = _open_osfhandle( (long)GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
    *stdout = *_fdopen( hConsole, "w" );
    setvbuf( stdout, NULL, _IONBF, 0 );
}

void closeConsoleWindow() {
    _close( hConsole );
}

 createConsoleWindow関数を呼ぶとコンソールウィンドウが新規に作られ即座にデスクトップに表示されます。アプリケーションを終了させるときなどにcloseConsoleWindow関数を呼ぶと後片付けをしてくれます。

 表示するだけなら簡単なのですが、この数行が何をしているかを見て見ると、これが結構ややこしい話になってしまいます。

 AllocConsole関数は呼び出したアプリケーションに新しいコンソールを割り当ててくれます。この関数を呼び出すとコンソールウィンドウがすぐに出るのですが、これだけだと何も書き込めないし表示もされません。C++の標準出力の関数とこのコンソールとが手を結んでいないからです。それを行うのが次の行以下です。

 続く_open_osfhandle関数はio.hで以下のように宣言されています:

_opne_osfhandle関数
int _open_osfhandle (
    intptr_t osfhandle,
    int flags
);

 この関数は第1引数のOSが提供するファイルハンドルをC言語のランタイムファイル記述子に関連付けます(MSDN: _open_osfhandle)…とMSDNには書かれています。小難しい表現が使われています。OSが提供するファイルハンドルというのはOSによって様々で、それをC言語のファイル記述子(ファイルアクセスの様式のようなもの)に関連付けてくれるというわけです。戻り値がその関連付けしたファイル記述子のハンドルとなります。

 上のコードでは、AllocConsole関数が作ってくれた標準出力(コンソール)のハンドルをGetStdHandle関数で取得し、第2引数でそれを「テキスト書き込み用」として使うと宣言しています。

 続く_fdopen関数というのは、ファイル記述子から「ストリームハンドル」というのを作ってくれます。ストリームというのは文字等を流し込む仕組みの事です。戻り値がそのハンドルとなるのですが、上のコードではそれを「*stdout」と標準出力用に定義されている関数ポインタ先に上書きしています。つまりここで標準出力(stdout関数)とコンソール出力用のストリーム関数とを直接結びつけた事になります。

 最後のsetvbuf関数はC言語の関数で、第1引数の標準出力が書き込むバッファを設定する関数です。第2引数に有効なメモリ領域(バッファ)を指定した場合、そこにバッファリングしますが、NULLにすると自動的にバッファを作ってくれます。第3引数はバッファリング方法を指定します。上の_IONBFはバッファリングをせずに出力がある度に標準出力に吐き出すよう指示するフラグです。他にも_IOFBF(バッファいっぱいまで溜める)、_IOLBF(1行分溜める)があります。第4引数はバッファサイズで、第2引数に自前のバッファを指定した場合はそのサイズを渡します。

 何だかややこしいわけですが(^-^;、上のコードを実行すると、まぁとりあえずはコンソールウィンドウが出てprintfで書き込みができます。_DEBUGなどでデバッグ時のみだけ出るようにするなど工夫すれば使えますね。