ウィンドウ メッセージ (Win32 と C++ を使用したはじめに)

GUI アプリケーションは、ユーザーとオペレーティング システムからのイベントに応答する必要があります。

  • ユーザーからのイベントには、 ユーザーがプログラムを操作できるすべての方法 (マウス クリック、キー ストローク、タッチ スクリーン ジェスチャなど) が含まれます。
  • オペレーティング システムからのイベントには、 プログラムの動作に影響を与える可能性があるプログラムの "外部" が含まれます。 たとえば、ユーザーが新しいハードウェア デバイスを接続したり、Windowsが低電力状態 (スリープまたは休止状態) に入ったりすることがあります。

これらのイベントは、プログラムの実行中にいつでも、ほぼすべての順序で発生する可能性があります。 実行フローを事前に予測できないプログラムをどのように構成しますか?

この問題を解決するために、Windowsはメッセージパッシング モデルを使用します。 オペレーティング システムは、アプリケーション ウィンドウにメッセージを渡すことによって、アプリケーション ウィンドウと通信します。 メッセージは、特定のイベントを指定する単なる数値コードです。 たとえば、ユーザーがマウスの左ボタンを押すと、次のメッセージ コードを含むメッセージがウィンドウに表示されます。

#define WM_LBUTTONDOWN    0x0201

一部のメッセージには、データが関連付けられています。 たとえば、 WM_LBUTTONDOWN メッセージには、マウス カーソルの x 座標と y 座標が含まれます。

ウィンドウにメッセージを渡すために、オペレーティング システムは、そのウィンドウに登録されているウィンドウ プロシージャを呼び出します。 (ウィンドウ プロシージャの目的がわかります)。

メッセージ ループ

アプリケーションは、実行中に何千ものメッセージを受信します。 (すべてのキーストロークとマウス ボタンのクリックでメッセージが生成されることを考慮してください)。さらに、アプリケーションには複数のウィンドウがあり、それぞれに独自のウィンドウ プロシージャがあります。 プログラムはこれらのメッセージをすべて受信し、正しいウィンドウプロシージャに配信する方法を教えてください。 アプリケーションでは、メッセージを取得して正しいウィンドウにディスパッチするためのループが必要です。

ウィンドウを作成するスレッドごとに、オペレーティング システムはウィンドウ メッセージのキューを作成します。 このキューには、そのスレッドで作成されたすべてのウィンドウのメッセージが保持されます。 キュー自体はプログラムに表示されません。 キューを直接操作することはできません。 ただし、 GetMessage 関数を呼び出すことで、キューからメッセージをプルできます。

MSG msg;
GetMessage(&msg, NULL, 0, 0);

この関数は、キューの先頭から最初のメッセージを削除します。 キューが空の場合、関数は別のメッセージがキューに登録されるまでブロックします。 GetMessage ブロックによってプログラムが応答しなくなるという事実。 メッセージがない場合は、プログラムが何も行う必要はありません。 バックグラウンド処理を実行する必要がある場合は、 GetMessage が別のメッセージを待機している間も実行を続ける追加のスレッドを作成できます。 ( 「ウィンドウの手順でボトルネックを回避する」を参照してください)。

GetMessage の最初のパラメーターは、MSG 構造体のアドレスです。 関数が成功すると、 MSG 構造体にメッセージに関する情報が入力されます。 これには、ターゲット ウィンドウとメッセージ コードが含まれます。 他の 3 つのパラメーターを使用すると、キューから取得するメッセージをフィルター処理できます。 ほとんどの場合、これらのパラメーターを 0 に設定します。

MSG 構造体にはメッセージに関する情報が含まれていますが、この構造を直接調べることはほとんどありません。 代わりに、他の 2 つの関数に直接渡します。

TranslateMessage(&msg); 
DispatchMessage(&msg);

TranslateMessage 関数は、キーボード入力に関連しています。 キーストローク (キーダウン、キーアップ) を文字に変換します。 この関数がどのように機能するかを実際に知る必要はありません。 DispatchMessage の前に呼び出してください。 興味がある場合は、MSDN ドキュメントへのリンクから詳細情報が提供されます。

DispatchMessage 関数は、メッセージのターゲットであるウィンドウのウィンドウ プロシージャを呼び出すようオペレーティング システムに指示します。 つまり、オペレーティング システムは、ウィンドウのテーブルでウィンドウ ハンドルを検索し、ウィンドウに関連付けられている関数ポインターを見つけて、関数を呼び出します。

たとえば、ユーザーがマウスの左ボタンを押すとします。 これにより、一連のイベントが発生します。

  1. オペレーティング システムは、メッセージ キューに WM_LBUTTONDOWN メッセージを配置します。
  2. プログラムは GetMessage 関数を呼び出します。
  3. GetMessage は、 キューからWM_LBUTTONDOWN メッセージをプルし、 MSG 構造体を入力します。
  4. プログラムは TranslateMessage 関数と DispatchMessage 関数を呼び出します。
  5. DispatchMessage 内では、オペレーティング システムによってウィンドウ プロシージャが呼び出されます。
  6. ウィンドウ プロシージャは、メッセージに応答するか、無視することができます。

ウィンドウ プロシージャが戻ると、 DispatchMessage に戻ります。 これにより、次のメッセージのメッセージ ループに戻ります。 プログラムが実行されている限り、メッセージはキューに引き続き到着します。 したがって、キューからメッセージを継続的にプルしてディスパッチするループが必要です。 ループは次のように考えることができます。

// WARNING: Don't actually write your loop this way.

while (1)      
{
    GetMessage(&msg, NULL, 0,  0);
    TranslateMessage(&msg); 
    DispatchMessage(&msg);
}

もちろん、書かれているように、このループは決して終わることはありません。 ここで 、GetMessage 関数の戻り値が入ります。 通常、 GetMessage は 0 以外の値を返します。 アプリケーションを終了してメッセージ ループから抜け出す場合は、 PostQuitMessage 関数を呼び出します。

        PostQuitMessage(0);

PostQuitMessage 関数は、メッセージ キューに WM_QUIT メッセージを配置します。 WM_QUIT は特殊なメッセージです。 GetMessage は 0 を返し、メッセージ ループの終了を通知します。 変更されたメッセージ ループを次に示します。

// Correct.

MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

GetMessage が 0 以外の値を返す限り、while ループの式は true と評価されます。 PostQuitMessage を呼び出すと、式は false になり、プログラムはループから抜け出します。 (この動作の興味深い結果の 1 つは、ウィンドウ プロシージャが WM_QUIT メッセージを受け取らないということです。したがって、このメッセージの case ステートメントをウィンドウ プロシージャに含める必要はありません)。

次に明らかな質問は、 PostQuitMessage を呼び出すタイミングです。 トピック「 ウィンドウを閉じる」でこの質問に戻りますが、最初にウィンドウプロシージャを記述する必要があります。

投稿メッセージと送信済みメッセージ

前のセクションでは、キューに入るメッセージについて説明しました。 場合によっては、オペレーティング システムはウィンドウ プロシージャを直接呼び出し、キューをバイパスします。

この区別の用語は、混乱を招く可能性があります。

  • メッセージを投稿すると、メッセージはメッセージ キューに送信され、メッセージ ループ (GetMessageDispatchMessage) を介してディスパッチされます。
  • メッセージを送信すると、メッセージはキューをスキップし、オペレーティング システムはウィンドウ プロシージャを直接呼び出します。

現時点では、違いはあまり重要ではありません。 ウィンドウ プロシージャはすべてのメッセージを処理します。 ただし、一部のメッセージはキューをバイパスし、ウィンドウ プロシージャに直接移動します。 ただし、アプリケーションがウィンドウ間で通信する場合は、違いが生じます。 この問題について詳しくは、「 メッセージとメッセージ キューについて」をご覧ください。

次へ

ウィンドウ プロシージャの記述