このページはゲームループの基礎講座シリーズの1つです。
今回は第1章の「WinMain関数」について説明します。(戻る)
はじめに
Window プログラミングの基礎であるグラフィカルなウインドウを出す手順は次のようになります。
- ウインドウ・クラスの登録
- ウインドウの作成
- メッセージ・ループの処理
上記のように大きく分けて3ステップあります。
このルールに基づきプログラミングすることで単純なウインドウを表示することが出来ます。
しかし、この3ステップだけで100行ほど長々とプログラムを書く必要があり、
初心者たちが記述量の多さから「何だか難しい!」と考えてしまいます。
でも、決まり文句と同じく一度打ち込んだソース・ファイルを使いまわすのが普通(コツ)です。
それでは雛型ウインドウを表示する決まり文句のソースコードを紹介します。
//------------------------------------------------------------------------------ // 第1章 WinMain関数 //------------------------------------------------------------------------------ #include <tchar.h> #include <Windows.h> //------------------------------------------------ // 記号定数(OK) //------------------------------------------------ #define ERRMSG_TITLE TEXT("WinMain関数") #define ERRMSG_WINREG TEXT("ウインドウ・クラスが登録できません。") #define ERRMSG_CREATE TEXT("ウインドウが作成できません。") //------------------------------------------------ // ウインドウ・クラスの登録 //------------------------------------------------ static ATOM funcWindowClass( HINSTANCE hInstance, LPCTSTR lpClassName ) { UNREFERENCED_PARAMETER( hInstance ); UNREFERENCED_PARAMETER( lpClassName ); return 0; } //------------------------------------------------ // ウインドウの作成 //------------------------------------------------ static HWND funcCreateWindow( HINSTANCE hInstance, LPCTSTR lpClassName, LPCTSTR lpTitleName, int nCmdShow ) { UNREFERENCED_PARAMETER( hInstance ); UNREFERENCED_PARAMETER( lpClassName ); UNREFERENCED_PARAMETER( lpTitleName ); UNREFERENCED_PARAMETER( nCmdShow ); return NULL; } //------------------------------------------------ // メイン関数(OK) //------------------------------------------------ extern int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) { LPCTSTR lpClassName = TEXT("Lesson1WndClass"); LPCTSTR lpTitleName = TEXT("第1章 WinMain関数"); MSG Msg; // ウインドウ・クラスの登録 if ( funcWindowClass(hInstance,lpClassName) == 0 ){ MessageBox( NULL, ERRMSG_WINREG, ERRMSG_TITLE, (MB_OK|MB_ICONERROR) ); return -1; } // ウインドウの作成 if ( funcCreateWindow(hInstance,lpClassName,lpTitleName,nCmdShow) == NULL ){ MessageBox( NULL, ERRMSG_CREATE, ERRMSG_TITLE, (MB_OK|MB_ICONWARNING) ); return -2; } // メッセージ・ループ while ( GetMessage(&Msg,NULL,0,0) > 0 ){ TranslateMessage( &Msg ); DispatchMessage( &Msg ); } UNREFERENCED_PARAMETER( hPrevInstance ); UNREFERENCED_PARAMETER( lpCmdLine ); return Msg.wParam; } //------------------------------------------------------------------------------ // End of Lesson1.cpp //------------------------------------------------------------------------------
上記のソースで重要な部分は _tWinMain 関数の部分です。
ここにグラフィカルなウインドウを出すための基本3ステップがあります。
ソースを見やすくするためにウインドウ・クラスの登録は funcWindowClass 関数に任せ、
ウインドウの作成は funcCreateWindow 関数に任せてます。
メッセージ・ループの処理だけは _tWinMain 関数に記述してます。
また、何らかのエラーが発生した場合にメッセージボックス(MessageBox)を出す記述もあります。
このメッセージボックスに出す文字列は記号定数の ERRMSG_ から始まるシンボルで定義されてます。
メッセージ内容を変更したい場合は TEXT() マクロの文字列を変更しましょう。
実行結果
実行するとエラーが表示されます。
これは funcWindowClass 関数が未完成でエラーを返すように記述してるからです。
実際に funcWindowClass 関数がエラーを返すと上記のようなメッセージボックスが表示されます。
WinMain関数
Windows OS 上で動くプログラムは最初に WinMain 関数を必ず実行します。
全てのプログラムは WinMain 関数から始まり WinMain 関数で終了します。
なお、WinMain 関数の他にも似たような関数名として wWinMain、_tWinMain があります。
この3つは次のような違いです。
- WinMain 関数は文字列の扱いをマルチバイト(シフトJIS)の文字コードを対象としてる。
- wWinMain 関数は文字列の扱いをユニコード(世界共通文字)の文字コードを対象としてる。
- _tWinMain 関数は文字列の扱いをコンパイル・オプションでマルチバイトやユニコードを選択できる。
このように文字列の扱いによって常に3タイプの記述があります。
しかし、通常ではコンパイル・オプションでマルチバイトとユニコードの文字セットを切り替え可能な方法で記述します。
つまり、_tWinMain 関数や TEXT() マクロを使って文字列を記述するだけで簡単に両方の文字セットに対応できます。
それでは3つのプロトタイプ宣言をご覧ください。
プロトタイプ宣言
int WINAPI WinMain( HINSTANCE hInstance, // 現在のインスタンス・ハンドル HINSTANCE hPrevInstance, // 以前のインスタンス・ハンドル LPSTR lpCmdLine, // コマンドラインの文字列 int nCmdShow // ウインドウの表示状態 ); int WINAPI wWinMain( HINSTANCE hInstance, // 現在のインスタンス・ハンドル HINSTANCE hPrevInstance, // 以前のインスタンス・ハンドル LPWSTR lpCmdLine, // コマンドラインの文字列 int nCmdShow // ウインドウの表示状態 ); int WINAPI _tWinMain( HINSTANCE hInstance, // 現在のインスタンス・ハンドル HINSTANCE hPrevInstance, // 以前のインスタンス・ハンドル LPTSTR lpCmdLine, // コマンドラインの文字列 int nCmdShow // ウインドウの表示状態 );
上記の着色された部分だけが違います。
この3つのデータ型は次のような意味です。
- LPSTR 型は CHAR 型の配列を用いて文字列を表現してる型でマルチバイト文字セットが対象。
- LPWSTR 型は WCHAR 型の配列を用いて文字列を表現してる型でユニコード文字セットが対象。
- LPTSTR 型はマルチバイト文字セット(CHAR)とユニコード文字セット(WCHAR)の両方に対応したデータ型です。
メッセージ・ループ
Windows OS はウインドウなどでイベントが発生した時にメッセージ(信号)を自動的に送ります。
イベントには色々あってウインドウをマウスでクリックした時、キーボードが押された時、タイトルバーで移動しようとした時などあります。
このような動作(イベント)を各ウインドウにメッセージ信号を送出しますが、このメッセージ信号を正しく処理しないと何も変化しません。
そこでメッセージ信号を正しく処理するために GetMessage 関数でメッセージ信号を取得します。
そしてメッセージ信号で仮想キーの場合は文字メッセージに変換する TranslateMessage 関数を呼び出します。
その後に実際にメッセージ信号を処理するウインドウ・プロシージャ関数を呼び出しますが、
この処理は専用の DispatchMessage 関数にメッセージ信号を渡すだけでウインドウ・プロシージャ関数が自動的に呼ばれます。
これでメッセージ信号を正しく処理するので再び GetMessage 関数でメッセージ信号を取得するためにループさせます。
この一連のループを「メッセージ・ループ」と呼び、絶対に必要不可欠な記述となります。
それではメッセージ・ループの部分だけを抜き出して表示します。
// メッセージ・ループ while ( GetMessage(&Msg,NULL,0,0) > 0 ){ TranslateMessage( &Msg ); DispatchMessage( &Msg ); }
上記のように GetMessage → TranslateMessage → DispatchMessage → GetMessage と繰り返してます。
このメッセージ・ループで「ゲームの進行」と「ゲームの描画」を行うのが、
ゲームループと呼ばれるゲーム関係の処理を繰り返し行うループとなります。
つまり、ゲームループの基礎がメッセージ・ループとなります。(つづく)
※コメント投稿者のブログIDはブログ作成者のみに通知されます