Win32 とメッセージ ループのしくみに関するいくつかの資料を読みましたが、まだ不明な点があります。メッセージ キューには正確に何が格納されているのでしょうか。メッセージに対応する整数値 ( WM_COMMAND
、WM_CREATE
など)、またはメッセージ整数値と 、 などの他のものを含む構造体へのMSG
ポインタ?wParam
lParam
3 に答える
あなたの質問に狭く答えるために、キュー内の各メッセージは、少なくとも、
- メッセージの送信先のウィンドウ ハンドル
- あなたがすでに正しく指摘したように、メッセージコード、wParamとlParam、
- で取得したメッセージが投稿された時刻
GetMessageTime()
、 - UI メッセージの場合、メッセージが投稿されたときのカーソルの位置( を参照
GetMessagePos()
)。
すべてのメッセージが実際にキューに格納されるわけではないことに注意してください。SendMessage()
ウィンドウを所有するスレッドからウィンドウに送信されたメッセージは決して保存されません。代わりに、レシーバ ウィンドウのメッセージ関数が直接呼び出されます。他のスレッドから送信されたメッセージは処理されるまで保存され、ウィンドウ関数を終了するか、明示的に を呼び出してメッセージが返信されるまで、送信スレッドはブロックされますReplyMessage()
。API 関数InSendMessage()
は、windows 関数が別のスレッドから送信されたメッセージを処理しているかどうかを判断するのに役立ちます。
ユーザーまたはシステムが投稿したメッセージは、いくつかの例外を除いて、キューに保存されます。
WM_TIMER メッセージが実際に保存されることはありません。代わりに
GetMessage()
、キューに他のメッセージがなく、タイマーが満期になった場合に、タイマー メッセージを作成します。これは、タイマー メッセージのデキューの優先度が最も低いこと、および短期間タイマーからの複数のメッセージがGetMessage()
しばらく呼び出されなくても、キューがオーバーフローしないことを意味します。その結果、そのタイマーからの最後の WM_TIMER メッセージが処理されてからタイマーが複数回経過した場合でも、指定されたタイマーに対して1 つのWM_TIMER メッセージが送信されます。同様に、WM_QUIT も格納されず、フラグが立てられるだけです。
GetMessage()
キューが使い果たされた後に WM_QUIT を取得したふりをし、これが取得する最後のメッセージです。もう 1 つの例は WM_PAINT メッセージです (これについては @cody-gray に感謝します)。このメッセージは、ウィンドウの一部¹ が「ダーティ」としてマークされ、再描画が必要な場合にもシミュレートされます。これも優先度の低いメッセージであり、GUI の応答性を低下させ、ちらつきを減らすために、キューが空になったときにウィンドウ内の複数の無効化された領域が一度に再描画されるように作成されています。を呼び出すと、すぐに再描画を強制できます
UpdateWindow()
。この関数SendMessage()
は、ウィンドウの露出部分が再描画されるまで戻らないという意味で、 のように動作します。明らかな最適化として、ウィンドウの無効な領域が空の場合、この関数はそのウィンドウに WM_PAINT を送信しません。
おそらく、他の例外と内部最適化があります。
で投稿されPostMessage()
たメッセージは、メッセージが投稿されたウィンドウを所有するスレッドのキューに入れられます。
メッセージが内部にどのような形式で保存されているかはわかりませんが、気にしません。Windows API はそれを完全に抽象化します。MSG 構造体は、GetMessage()
またはに渡すメモリに書き込まれますPeekMessage()
。Windows SDK ガイドに記載されている以上の内部実装の詳細について知る必要も、心配する必要もありません。
¹ WM_PAINT と WM_TIMER が互いにどの程度正確に優先順位を付けられているかはわかりません。WM_PAINT の優先度は低いと思いますが、間違っている可能性があります。
Windows のメッセージ キューは抽象化されています。これをキューと考えると非常に便利ですが、実際の実装はもっと詳細です。Windows には、4 つの異なるメッセージ ソースがあります。
SendMessage() によって配信されるメッセージ。Windows はウィンドウ プロシージャを直接呼び出します。メッセージは Peek/GetMessage() によって返されませんが、その関数を呼び出してディスパッチする必要があります。ほとんどのメッセージはこの方法で配信されます。WM_COMMAND はそのようなもので、TranslateAccelerator() のようなキーダウン イベントを変換するコードによって直接送信されます。キューのような動作はありません。
ウィンドウ状態から合成されたメッセージ。最良の例は WM_PAINT で、「ウィンドウにダーティな四角形がある」状態フラグが設定されている場合に配信されます。WM_TIMER は、「タイマーが期限切れになった」状態フラグが設定されたときに配信されます。これらは「優先度の低い」メッセージであり、メッセージ キューが空の場合にのみ配信されます。それらは GetMessage() によって配信されますが、それ以外の場合はキューに存在しません。
キーボードとマウスのイベント メッセージを入力します。これらは、真にキューのような動作を持つメッセージです。キーボードの場合、これにより先行入力が機能し、プログラムがキーストロークを受け入れる準備ができていないときにキーストロークが失われることがなくなります。一連の状態がそれに関連付けられています。たとえば、キーボードの状態全体がコピーされます。状態が少ないことを除いて、マウスの場合とほぼ同じです。WM_MOUSEMOVE メッセージは、興味深いコーナー ケースです。キューは、カーソルが通過するすべてのピクセルを格納するわけではありません。位置の変更は 1 つのメッセージに蓄積され、必要に応じて保存または配信されます。
明示的な PostMessage() 呼び出しによってキューに格納されたメッセージ。それは完全にプログラム コード次第です。明らかに、GetMessage() 時に正確に再生できるように、呼び出しの引数と呼び出しが行われた時刻を格納するだけで済みます。
MSDNには、メッセージとメッセージキューのすべてを説明するすばらしい記事があります。
しかし、あなたの質問に答えるために、キューはウィンドウごとに存在し、メッセージとそれに関連するパラメータを一時的に保存します。そのキューMSG
が実装で定義されているかどうかは関係ありませんが、おそらく(または同様のもの)です。また、すべてのメッセージがキューに送られるわけではなく、すぐに処理する必要があるメッセージもあることに注意してください。