前回までは、ウインドウが生成された後、一瞬でウインドウが閉じてしまうアプリケーションでした。 今回は、ウインドウ右上(タイトルバー右)の閉じるボタンを押して終了するようにします。
メッセージループと、ウィンドウプロシージャというものを使用します。
前回までは、WinMain() が最後の return 0; まで来る事により、アプリケーションが終了していましたが、今回は、メッセージループを WinMain() に入れることにより、
ユーザが終了処理をしない限り、このメッセージループから抜けないようにします。
メッセージループについては、後述します。
ウインドウクラスの登録 - インコのWindowsSDK を修正します。
流れ
メッセージ ループ
アプリケーションは、実行中に、 ユーザーがキーを押した、マウスを移動した、メニューを実行したなどの動作が行われたかどうかを 調べるループを持っています。このループをメッセージループと呼びます。
ユーザーがキーを押した、マウスを移動した、メニューを実行したなどの動作は、メッセージと言う形で、 やりとりしています。
メッセージは整数の値です。普通は、整数の値そのままで使わず、WM_から始まるシンボルを使います。
例えば、「ウインドウ破棄のメッセージ」は、WM_DESTROYです。
#define WM_DESTROY 0x0002
と定義されていますので、実際の値は2です。
1.GetMessage()でメッセージを取得。
GetMessage()は、WM_QUITというメッセージがない限り、true (=0以外)を返します。
WM_QUITメッセージを受けとれば、メッセージループから抜けて、アプリケーションが終了させます。
BOOL GetMessage( LPMSG lpMsg, // MSG構造体へのポインタ HWND hWnd, // メッセージを取得するウィンドウのハンドル UINT wMsgFilterMin, // 取得するメッセージの範囲の最小値 UINT wMsgFilterMax // 取得するメッセージの範囲の最大値 );
MSG構造体は、ウィンドウ メッセージに関する情報を管理する構造体です。
typedef struct { HWND hwnd; // メッセージの対象となるウィンドウのハンドル UINT message; // メッセージ コード WPARAM wParam; // メッセージの付加情報 LPARAM lParam; // メッセージの付加情報 DWORD time; // メッセージがポストされた時間 POINT pt; // メッセージがポストされたときのカーソル位置 } MSG, *PMSG;
MSG構造体の3つ目と4つ目のメンバの型、WPARAM、LPARAMは、ともにUINT_PTRであり、ポインタ型です。
32ビットの環境の場合、32ビットのデータです。
WPARAM、LPARAMは、ウィンドウ メッセージ(MSG構造体2つ目のメンバ : message)の付加情報です。
例えば「キーが押された」という情報だけですと、情報不足なので、足りない部分はWPARAM、LPARAMで補っています。
ウィンドウ メッセージによって、WPARAM、LPARAMに入る値は異なります。
2.押されているキーボードの情報を、アプリケーションが取得できる形に変換してくれる関数TranslateMessage()を実行します。
BOOL TranslateMessage( const MSG *lpMsg // MSG構造体へのポインタ );
3.取得したメッセージを、ウインドウプロシージャに送る処理をするDispatchMessage()を実行します。
LRESULT DispatchMessage( const MSG *lpMsg // MSG構造体へのポインタ );
4.WinMain()の戻り値を、MSG構造体のwParamパラメータにする。
ウィンドウ クラス
WNDCLASSEX構造体のlpfnWndProcメンバを、後述のウィンドウプロシージャにします。
これにより、メッセージの処理をウィンドウプロシージャで行います。
WNDCLASS構造体は、ウィンドウクラスに関する情報を格納する構造体です。
入門3:ウインドウクラスの登録
で解説しました。
ウィンドウプロシージャ
ウィンドウプロシージャとは、メッセージループで取得したメッセージを処理する関数です。
ウィンドウプロシージャの名前は任意です。
LRESULT CALLBACK ######( HWND hWnd, // メッセージが発生したウィンドウのハンドル UINT message, // メッセージ コード WPARAM wParam, // メッセージの付加情報(メッセージコードにより異なります) LPARAM lParam // メッセージの付加情報(メッセージコードにより異なります) );
戻り値のLRESULTはLONG_PTR型、すなわちポインタ型のことです。
CALLBACKは、呼び出し規約(スタックというメモリ領域の利用の仕方を決めたもの)です。
#define CALLBACK __stdcall
とLpmApi.hで定義されています。したがってCALLBACKは、__stdcallと同じ意味です。WINAPIも__stdcallと定義されていますので、同じ呼び出し規約です。
CALLBACKをつけた関数は、コールバック関数と呼ばれますが、メッセージを処理する関数の場合は、ウィンドウプロシージャと呼ぶことが多いです。
wParamと、lParamは、メッセージの付加情報です。2つ目の引数messageによって、何が入るかは変わってきます。
1.ウィンドウプロシージャの2つめの引数messageによって、メッセージを判断します。
2.ウインドウ右上の閉じるボタンを押した際に、WM_DESTROYメッセージが来るので、
このときに、PostQuitMessage()を実行して、ウインドウを閉じます。
PostQuitMessage()を実行すると、WM_QUITメッセージを発生させます。
これにより、メッセージループから抜け出すことができて、ウインドウを閉じることができるようになります。
VOID PostQuitMessage( int nExitCode // WM_QUITのwParamの値 );
3.ウィンドウプロシージャが処理しない部分では、デフォルトウィンドウプロシージャDefWindowProc()を実行
アプリケーションが処理しないメッセージをDefWindowProc()に渡せば、Windows側で勝手に処理してくれます。
DefWindowProc()を実行しないと、基本的なウィンドウの動作ができなくなります。
LRESULT DefWindowProc( HWND hWnd, // ウィンドウのハンドル UINT Msg, // メッセージのID(識別子) WPARAM wParam, // メッセージの付加情報(引数Msgにより異なります) LPARAM lParam // メッセージの付加情報(引数Msgにより異なります) );
ソースコードの入力
ソースコードは下記のように入れてください。
#include <windows.h> // このコード モジュールに含まれる関数の宣言を転送します: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; MyRegisterClass(hInstance); // アプリケーションの初期化を実行します: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } // メイン メッセージ ループ: while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } // // 関数: MyRegisterClass() // // 目的: ウィンドウ クラスを登録します。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(NULL , IDI_APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = TEXT("HP"); wcex.hIconSm = LoadIcon(NULL , IDI_APPLICATION); return RegisterClassEx(&wcex); } // // 関数: InitInstance(HINSTANCE, int) // // 目的: メイン ウィンドウを作成します。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(TEXT("HP"), TEXT("HP"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 関数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: メイン ウィンドウのメッセージを処理します。 // // WM_DESTROY - 中止メッセージを表示して戻る // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }