パソコンでプログラミングしよう ウィンドウズC++プログラミング環境の構築
1.6.3.6(15)
イベント駆動型プログラミング

イベント駆動型プログラミングはイベントを待機し、発生したイベントに従い処理を行うプログラミングパラダイム

その他の外部情報

本サイトでの解釈

イベント駆動型プログラミングの文脈において"イベント"とはプログラムフローが外部から受け取る情報である。MS-DOSのメモリ常駐型プログラムはINT21H割り込みでイベント駆動を実現していたと見なせる(PC98背景のグラフィック画面でキー押下の度にジャンプするマリオをあなたは覚えているだろうか)。95より古いウインドウズはノンプリエンプティブ・マルチタスクで、オペレーティングシステム含む全プログラムがメモリ空間を共有する単一プロセスでイベントは容易に実現できる。今はプリエンプティブ・マルチタスクで全プログラムはオペレーティングシステム管理下に独立したメモリ空間を持ち、プログラムフローはプロセス(厳密にはスレッド)、外部はオペレーティングシステム含む別プロセス(スレッド)、イベントは何らかのプロセス(スレッド)間通信と見なせる。

ウィンドウズのイベント駆動プログラミング

ウィンドウズの主要なイベントは"メッセージ"と"カーネルオブジェクト"(C++オブジェクトとは関係ない)であるが、通常はGUIを実現するメッセージベースのイベント駆動プログラミングを扱う。カーネルオブジェクトベースのイベント駆動プログラミングは可能だがその必要性は限られる。カーネルオブジェクトの一つに"イベント"があり、これは一般に"イベント"として総称される物の一つに過ぎない。すなわちプログラミング一般の文脈で"イベント"と"メッセージ"はほぼ同義だが、ウィンドウズAPIの文脈で両者は全くの別物である。

以降、ウィンドウズAPIの文脈で説明する。オペレーティングシステムはGUI操作などをメッセージとしてアプリケーションのメッセージキューへ格納する。プログラムフローはメッセージループでメッセージをキューより取得してその情報に基づき処理を行う。ウィンドウズメッセージは処理を行うウィンドウ(コントロール)ハンドル(識別ID)を持ち、メッセージループは処理をそれへ委ねる(ディスパッチ)。

MSG msg;
while(::GetMessage(&msg,NULL,0,0)) // メッセージ取得
{
::TranslateMessage(&msg); // キー入力であれば対応する文字入力に変換する
::DispatchMessage(&msg); // ディスパッチ
}

ウィンドウズメッセージ

ウィンドウズメッセージの構造を示す。

typedef struct tagMSG {
HWND hwnd; // 処理を行うウィンドウ(コントロール)ハンドル
UINT message; // メッセージID
WPARAM wParam; // 追加情報
LPARAM lParam; // 追加情報
DWORD time; // ポストされた時刻
POINT pt; // ポストされた時刻のマウスカーソル座標
DWORD lPrivate; // 不明、恐らくシステム予約
} MSG, *PMSG, *NPMSG, *LPMSG;

メッセージはさらに"通知"(notification)と"メッセージ(狭義)"に分類される。"通知"はオペレーティングシステムがウィンドウ(コントロール)の状態変化(主にはGUI操作)をアプリケーションに送付するもので、メッセージキューに格納されるメッセージは全て"通知"である。例えばWM_MOVEはウィンドウが移動した結果としてメッセージキューに格納され、アプリケーションはウィンドウ移動に伴う処理を行う。"メッセージ(狭義)"はアプリケーションがウィンドウへ送付して操作/情報取得し、例えばWM_SETTEXT/WM_GETTEXTはウィンドウ表示文字列を設定/取得する。このようにアプリケーションから見て両者の送付方向は反対で、イベント駆動にあずかるメッセージは"通知"のみと言う事になる。通常のアプリケーションは"通知"を状態変化したウィンドウへディスパッチする。

覚え書き
ただしウィンドウズAPIの文脈はほとんどの場合に"通知"を"メッセージ"として説明する。

PostMessage関数はウィンドウの属するスレッドのメッセージキューに格納(ポスト)し、SendMessage関数はウィンドウに直接メッセージを送付する。アプリケーションは"通知"をポスト/送付してウィンドウの状態変化を模擬できる。アプリケーションは"メッセージ(狭義)"を送付してウィンドウを直接操作する。

BOOL PostMessageW(
HWND hWnd, // 処理を行うウィンドウ(コントロール)ハンドル
UINT Msg, // メッセージID
WPARAM wParam, // 追加情報
LPARAM lParam // 追加情報
);
LRESULT SendMessageW(
... // PostMessageと同じ
);