メッセージとメッセージ キューについて
MS-DOS ベースのアプリケーションとは異なり、Windows ベースのアプリケーションはイベント ドリブンです。 入力を取得するために明示的な関数呼び出し (C ランタイム ライブラリ呼び出しなど) は行いません。 代わりに、システムが入力を渡すのを待ちます。
システムは、アプリケーションのすべての入力をアプリケーションのさまざまなウィンドウに渡します。 各ウィンドウには、ウィンドウ プロシージャと呼ばれる関数があり、ウィンドウに対する入力がある場合は常にシステムが呼び出します。 ウィンドウ プロシージャは入力を処理し、システムに制御を返します。 ウィンドウ プロシージャの詳細については、「 ウィンドウ プロシージャ」を参照してください。
最上位のウィンドウが数秒間以上メッセージへの応答を停止した場合、システムはウィンドウが応答していないと見なします。 この場合、システムはウィンドウを非表示にし、同じ Z オーダー、位置、サイズ、およびビジュアル属性を持つゴースト ウィンドウに置き換えます。 これにより、ユーザーはそれを移動したり、サイズを変更したり、アプリケーションを閉じることもできます。 ただし、アプリケーションが実際に応答していないため、使用できるアクションはこれらだけです。 デバッガー モードの場合、システムはゴースト ウィンドウを生成しません。
このセクションでは、次のトピックについて説明します。
- Windows メッセージ
- メッセージ型
- メッセージ ルーティング
- メッセージ処理
- メッセージのフィルター処理
- メッセージの投稿と送信
- メッセージのデッドロック
- メッセージのブロードキャスト
- メッセージのクエリ
Windows メッセージ
システムは 、メッセージの形式でウィンドウ プロシージャに入力を渡します。 メッセージは、システムとアプリケーションの両方によって生成されます。 ユーザーが入力、マウスの移動、スクロール バーなどのコントロールのクリックなど、各入力イベントでメッセージを生成します。 また、アプリケーションがシステム フォント リソースのプールを変更したり、ウィンドウのサイズを変更したりしたときなど、アプリケーションによって発生したシステムの変更に応じてメッセージも生成されます。 アプリケーションは、タスクを実行したり、他のアプリケーションのウィンドウと通信したりするために、独自のウィンドウを指示するメッセージを生成できます。
システムは、ウィンドウ ハンドル、メッセージ識別子、およびメッセージ パラメーターと呼ばれる 2 つの値の 4 つのパラメーターのセットを使用して、 ウィンドウ プロシージャにメッセージを送信します。 ウィンドウ ハンドルは、メッセージの対象となるウィンドウを識別します。 システムはそれを使用して、メッセージを受け取るウィンドウ・プロシージャーを判別します。
メッセージ識別子は、メッセージの目的を識別する名前付き定数です。 ウィンドウ プロシージャは、メッセージを受信すると、メッセージ識別子を使用してメッセージの処理方法を決定します。 たとえば、メッセージ識別子 WM_PAINT は、ウィンドウのクライアント領域が変更され、再描画する必要があることをウィンドウ プロシージャに通知します。
メッセージ パラメーターは、メッセージの処理中にウィンドウ プロシージャによって使用されるデータまたはデータの場所を指定します。 メッセージ パラメーターの意味と値は、メッセージによって異なります。 メッセージ パラメーターには、整数、パックされたビット フラグ、追加データを含む構造体へのポインターなどを含めることができます。 メッセージがメッセージ パラメーターを使用しない場合、通常は NULL に設定されます。 ウィンドウ プロシージャでは、メッセージ識別子をチェックして、メッセージ パラメーターの解釈方法を決定する必要があります。
メッセージ型
このセクションでは、次の 2 種類のメッセージについて説明します。
System-Defined メッセージ
システムは、アプリケーションと通信するときに システム定義メッセージ を送信またはポストします。 これらのメッセージを使用して、アプリケーションの操作を制御し、アプリケーションが処理するための入力やその他の情報を提供します。 アプリケーションは、システム定義メッセージを送信またはポストすることもできます。 アプリケーションでは通常、これらのメッセージを使用して、事前登録されたウィンドウ クラスを使用して作成されたコントロール ウィンドウの操作を制御します。
各システム定義メッセージには、メッセージの目的を示す一意のメッセージ識別子と、対応するシンボリック定数 (ソフトウェア開発キット (SDK) ヘッダー ファイルで定義) があります。 たとえば、 WM_PAINT 定数は、ウィンドウがその内容を描画することを要求します。
シンボリック定数は、システム定義メッセージが属するカテゴリーを指定します。 定数のプレフィックスは、メッセージを解釈して処理できるウィンドウの種類を識別します。 プレフィックスと関連するメッセージ カテゴリを次に示します。
Prefix | メッセージ カテゴリ | ドキュメント |
---|---|---|
ABM と ABN | アプリケーション デスクトップ ツール バー | シェル メッセージと通知 |
ACM と ACN | アニメーション コントロール | アニメーション コントロール メッセージ と アニメーション コントロール通知 |
BCM、BCN、BM、BN | Button コントロール | ボタン コントロール メッセージ と ボタン コントロール通知 |
CB と CBN | ComboBox コントロール | ComboBox コントロール メッセージ と ComboBox コントロール通知 |
CBEM と CBEN | ComboBoxEx コントロール | ComboBoxEx メッセージ と ComboBoxEx 通知 |
Ccm | 一般的なコントロール | メッセージの制御 |
CDM | [共通] ダイアログ ボックス | [共通] ダイアログ ボックス のメッセージ |
Dfm | 既定のコンテキスト メニュー | シェル メッセージと通知 |
DL | リスト ボックスをドラッグする | リスト ボックスの通知をドラッグする |
DM | 既定のプッシュ ボタン コントロール | ダイアログ ボックス メッセージ |
DTM と DTN | 日付と時刻の選択コントロール | 日付と時刻の選択メッセージ と 日付と時刻の選択の通知 |
EM と EN | 編集コントロール | コントロール メッセージの編集、 コントロール通知の編集、 リッチ エディット メッセージ、 リッチ エディット通知 |
HDM と HDN | ヘッダー コントロール | ヘッダー コントロール メッセージ と ヘッダー コントロール通知 |
HKM | ホット キー コントロール | ホット キー制御メッセージ |
IPM と IPN | IP アドレス コントロール | IP アドレス メッセージ と IP アドレス通知 |
LB と LBN | リスト ボックス コントロール | リスト ボックス メッセージ と リスト ボックス通知 |
LM | SysLink コントロール | SysLink コントロール メッセージ |
LVM と LVN | リスト ビュー コントロール | リスト ビュー メッセージ と リスト ビュー通知 |
MCM と MCN | 月の予定表コントロール | 月の予定表のメッセージ と 月の予定表の通知 |
PBM (PBM) | 進行状況バー | 進行状況バーのメッセージ |
PGM と PGN | ポケットベル コントロール | ポケットベル コントロール メッセージ と ポケットベル コントロール通知 |
PSM と PSN | プロパティ シート | プロパティ シートのメッセージ と プロパティ シートの通知 |
RB と RBN | Rebar コントロール | Rebar コントロール メッセージ と Rebar コントロール通知 |
SB と SBN | ステータス バー ウィンドウ | ステータス バーのメッセージ と ステータス バーの通知 |
Sbm | スクロール バー コントロール | スクロール バーメッセージ |
Smc | シェル メニュー | シェル メッセージと通知 |
STM と STN | 静的コントロール | 静的コントロール メッセージ と 静的コントロール通知 |
TB と TBN | ツール バー | ツール バー コントロール メッセージ と ツール バー コントロール通知 |
TBM と TRBN | トラックバー コントロール | トラックバーコントロールメッセージ と トラックバーコントロール通知 |
TCM と TCN | タブ コントロール | タブ コントロール メッセージ と タブ コントロール通知 |
TDM と TDN | [タスク] ダイアログ | タスク ダイアログ メッセージ と タスク ダイアログ通知 |
TTM と TTN | ツールヒント コントロール | ヒント コントロール メッセージ と ヒント コントロール通知 |
TVM と TVN | ツリー ビュー コントロール | ツリー ビュー メッセージ と ツリー ビュー通知 |
UDM と UDN | アップダウン コントロール | アップダウン メッセージ と アップダウン通知 |
Wm | 全般 |
一般的なウィンドウ メッセージは、マウスとキーボード入力のメッセージ、メニューとダイアログ ボックスの入力、ウィンドウの作成と管理、動的データ交換 (DDE) など、さまざまな情報と要求に対応します。
メッセージのApplication-Defined
アプリケーションは、独自のウィンドウで使用したり、他のプロセスのウィンドウと通信したりするためのメッセージを作成できます。 アプリケーションが独自のメッセージを作成する場合は、メッセージを受け取るウィンドウ プロシージャがメッセージを解釈し、適切な処理を提供する必要があります。
メッセージ識別子の値は次のように使用されます。
- システムは、システム定義メッセージの0x03FF (WM_USER - 1) を介して 0x0000 範囲内のメッセージ ID 値を予約します。 アプリケーションでは、プライベート メッセージにこれらの値を使用できません。
- 0x0400範囲の値 ( WM_USERの値) から0x7FFFは、プライベート ウィンドウ クラスのメッセージ識別子に使用できます。
- アプリケーションがバージョン 4.0 とマークされている場合は、プライベート メッセージの0xBFFF 0x8000 (WM_APP) の範囲内でメッセージ識別子の値を使用できます。
- システムは、アプリケーションが RegisterWindowMessage 関数を呼び出してメッセージを登録するときに、0xFFFF 0xC000範囲内のメッセージ識別子を返します。 この関数によって返されるメッセージ識別子は、システム全体で一意であることが保証されます。 この関数を使用すると、他のアプリケーションが異なる目的で同じメッセージ識別子を使用する場合に発生する可能性がある競合を防ぐことができます。
メッセージのルーティング
システムは、メッセージをウィンドウ プロシージャにルーティングする 2 つの方法を使用します。メッセージをメッセージ キューと呼ばれる先入れ先出し キュー、メッセージを一時的に格納するシステム定義のメモリ オブジェクト、およびウィンドウ プロシージャにメッセージを直接送信します。
メッセージ キューにポストされるメッセージは、 キューに登録されたメッセージと呼ばれます。 これらは主に、WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_KEYDOWN、WM_CHARメッセージなど、マウスまたはキーボードを介して入力されたユーザー入力の結果です。 キューに入れられたその他のメッセージには、タイマー、ペイント、終了メッセージ ( WM_TIMER、 WM_PAINT、 およびWM_QUIT) が含まれます。 ウィンドウ プロシージャに直接送信される他のほとんどのメッセージは、 非キューメッセージと呼ばれます。
キューに入ったメッセージ
システムは、一度に任意の数のウィンドウを表示できます。 マウスとキーボードの入力を適切なウィンドウにルーティングするために、システムはメッセージ キューを使用します。
システムは、GUI スレッドごとに 1 つのシステム メッセージ キューと 1 つのスレッド固有メッセージ キューを保持します。 GUI 以外のスレッドのメッセージ キューを作成するオーバーヘッドを回避するために、すべてのスレッドが最初にメッセージ キューなしで作成されます。 システムは、スレッドが特定のユーザー関数の 1 つを最初に呼び出す場合にのみ、スレッド固有のメッセージ キューを作成します。GUI 関数呼び出しは行われません。その結果、メッセージ キューが作成されます。
ユーザーがマウスを移動したり、マウス ボタンをクリックしたり、キーボードの種類をクリックしたりするたびに、マウスまたはキーボードのデバイス ドライバーによって入力がメッセージに変換され、システム メッセージ キューに配置されます。 システムは、一度に 1 つずつメッセージをシステム メッセージ キューから削除し、メッセージを調べて宛先ウィンドウを特定し、宛先ウィンドウを作成したスレッドのメッセージ キューにメッセージをポストします。 スレッドのメッセージ キューは、スレッドによって作成されたウィンドウのすべてのマウス メッセージとキーボード メッセージを受け取ります。 スレッドはキューからメッセージを削除し、処理のために適切なウィンドウ プロシージャに送信するようにシステムに指示します。
WM_PAINT メッセージ、WM_TIMER メッセージ、およびWM_QUIT メッセージを除き、システムは常にメッセージ キューの末尾にメッセージを投稿します。 これにより、ウィンドウが適切な最初の入力、先出し (FIFO) シーケンスで入力メッセージを受け取ります。 ただし、WM_PAINT メッセージ、WM_TIMER メッセージ、およびWM_QUIT メッセージはキューに保持され、キューに他のメッセージが含まれない場合にのみウィンドウ プロシージャに転送されます。 さらに、同じウィンドウ の複数のWM_PAINT メッセージが 1 つの WM_PAINT メッセージに結合され、クライアント領域のすべての無効な部分が 1 つの領域に統合されます。 WM_PAINTメッセージを組み合わせると、ウィンドウがクライアント領域の内容を再描画する必要がある回数が減ります。
システムは、 MSG 構造体を入力してメッセージ キューにコピーすることで、メッセージをスレッドのメッセージ キューにポストします。 MSG の情報には、メッセージの対象となるウィンドウのハンドル、メッセージ識別子、2 つのメッセージ パラメーター、メッセージが投稿された時刻、およびマウス カーソル位置が含まれます。 スレッドは、 PostMessage 関数または PostThreadMessage 関数を使用して、メッセージを独自のメッセージ キューまたは別のスレッドのキューに投稿できます。
アプリケーションは 、GetMessage 関数を使用してキューからメッセージを削除できます。 キューからメッセージを削除せずにメッセージを調べるには、アプリケーションで PeekMessage 関数を使用できます。 この関数は 、メッセージ に関する情報を MSG に入力します。
キューからメッセージを削除した後、アプリケーションは DispatchMessage 関数を使用して、処理のためにメッセージをウィンドウ プロシージャに送信するようにシステムに指示できます。 DispatchMessage は、GetMessage または PeekMessage 関数の以前の呼び出しによって入力された MSG へのポインターを受け取ります。 DispatchMessage は 、ウィンドウ ハンドル、メッセージ識別子、および 2 つのメッセージ パラメーターをウィンドウ プロシージャに渡しますが、メッセージが投稿された時刻やマウス カーソル位置は渡されません。 アプリケーションは、メッセージの処理中に GetMessageTime 関数と GetMessagePos 関数を呼び出すことによって、この情報を取得できます。
スレッドは WaitMessage 関数を使用して、メッセージ キューにメッセージがない場合に他のスレッドに制御を提供できます。 関数はスレッドを中断し、新しいメッセージがスレッドのメッセージ キューに配置されるまでは返されません。
SetMessageExtraInfo 関数を呼び出して、値を現在のスレッドのメッセージ キューに関連付けることができます。 次に 、GetMessageExtraInfo 関数を呼び出して、 GetMessage または PeekMessage 関数によって取得された最後のメッセージに関連付けられた値を取得します。
キューに入れされていないメッセージ
キューに登録されていないメッセージは、システム メッセージ キューとスレッド メッセージ キューをバイパスして、宛先ウィンドウ プロシージャに直ちに送信されます。 システムは通常、キューに入れされていないメッセージを送信して、そのメッセージに影響を与えるイベントのウィンドウに通知します。 たとえば、ユーザーが新しいアプリケーション ウィンドウをアクティブ化すると、WM_ACTIVATE、WM_SETFOCUS、WM_SETCURSORなどの一連のメッセージがウィンドウに送信されます。 これらのメッセージは、ウィンドウがアクティブ化されていること、キーボード入力がウィンドウに送信されていること、およびマウス カーソルがウィンドウの境界線内で移動されたことを通知します。 キューに入れられなかったメッセージは、アプリケーションが特定のシステム関数を呼び出したときにも発生する可能性があります。 たとえば、システムは、アプリケーションが SetWindowPos 関数を使用してウィンドウを移動した後に、WM_WINDOWPOSCHANGED メッセージを送信します。
キューに入れないメッセージを送信する関数には、 BroadcastSystemMessage、 BroadcastSystemMessageEx、 SendMessage、 SendMessageTimeout、 SendNotifyMessage があります。
メッセージ処理
アプリケーションは、スレッドのメッセージ キューにポストされたメッセージを削除して処理する必要があります。 シングルスレッド アプリケーションでは、通常、WinMain 関数のメッセージ ループを使用して、処理のために適切なウィンドウ プロシージャにメッセージを削除して送信します。 複数のスレッドを持つアプリケーションでは、ウィンドウを作成する各スレッドにメッセージ ループを含めることができます。 次のセクションでは、メッセージ ループのしくみと、ウィンドウ プロシージャの役割について説明します。
メッセージ ループ
単純なメッセージ ループは、GetMessage、TranslateMessage、DispatchMessage の 3 つの関数それぞれに対する 1 つの関数呼び出しで構成されます。 エラーが発生した場合、 GetMessage は –1 を返すので、特別なテストが必要であることに注意してください。
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GetMessage 関数は、キューからメッセージを取得し、MSG 型の構造体にコピーします。 WM_QUIT メッセージが発生しない限り、0 以外の値を返します。その場合は FALSE を返し、ループを終了します。 シングル スレッド アプリケーションでは、多くの場合、メッセージ ループを終了することが、アプリケーションを閉じる最初の手順です。 アプリケーションは、PostQuitMessage 関数を使用して独自のループを終了できます。通常は、アプリケーションのメイン ウィンドウのウィンドウ プロシージャのWM_DESTROY メッセージに応答します。
GetMessage の 2 番目のパラメーターとしてウィンドウ ハンドルを指定した場合、指定されたウィンドウのメッセージのみがキューから取得されます。 GetMessage では、キュー内のメッセージをフィルター処理して、指定した範囲内にあるメッセージのみを取得することもできます。 メッセージのフィルター処理の詳細については、「 メッセージのフィルター処理」を参照してください。
スレッドがキーボードから文字入力を受け取る場合は、スレッドのメッセージ ループに TranslateMessage を含める必要があります。 ユーザーがキーを押すたびに、仮想キー メッセージ (WM_KEYDOWN および WM_KEYUP) が生成されます。 仮想キー メッセージには、押されたキーを識別する仮想キー コードが含まれていますが、その文字値は識別しません。 この値を取得するには、メッセージ ループに TranslateMessage が含まれている必要があります。これにより、仮想キー メッセージが文字メッセージ (WM_CHAR) に変換され、アプリケーション メッセージ キューに戻されます。 その後、メッセージ ループの後続の反復時に文字メッセージを削除し、ウィンドウ プロシージャにディスパッチできます。
DispatchMessage 関数は、MSG 構造体で指定されたウィンドウ ハンドルに関連付けられているウィンドウ プロシージャにメッセージを送信します。 ウィンドウ ハンドルが HWND_TOPMOST場合、 DispatchMessage は システム内のすべての最上位ウィンドウのウィンドウ プロシージャにメッセージを送信します。 ウィンドウ ハンドルが NULL の場合、 DispatchMessage はメッセージに対して何も行いません。
アプリケーションのメイン スレッドは、アプリケーションを初期化し、少なくとも 1 つのウィンドウを作成した後、メッセージ ループを開始します。 メッセージ ループが開始されると、スレッドのメッセージ キューからメッセージが引き続き取得され、適切なウィンドウにディスパッチされます。 メッセージ ループは 、GetMessage 関数がメッセージ キューから WM_QUIT メッセージを削除すると終了します。
アプリケーションに多数のウィンドウが含まれている場合でも、メッセージ キューに必要なメッセージ ループは 1 つだけです。 DispatchMessage は 常にメッセージを適切なウィンドウにディスパッチします。これは、キュー内の各メッセージが、メッセージが属するウィンドウのハンドルを含む MSG 構造体であるためです。
メッセージ ループは、さまざまな方法で変更できます。 たとえば、キューからメッセージをウィンドウにディスパッチせずに取得できます。 これは、ウィンドウを指定しないメッセージを投稿するアプリケーションに役立ちます。 また、 GetMessage に特定のメッセージを検索するように指示し、他のメッセージをキューに残すこともできます。 これは、メッセージ キューの通常の FIFO 順序を一時的にバイパスする必要がある場合に便利です。
アクセラレータ キーを使用するアプリケーションは、キーボード メッセージをコマンド メッセージに変換できる必要があります。 これを行うには、アプリケーションのメッセージ ループに TranslateAccelerator 関数の呼び出しを含める必要があります。 アクセラレータ キーの詳細については、「 キーボード アクセラレータ」を参照してください。
スレッドがモードレス ダイアログ ボックスを使用する場合、ダイアログ ボックスがキーボード入力を受け取ることができるように、メッセージ ループに IsDialogMessage 関数を含める必要があります。
Window プロシージャ
ウィンドウ プロシージャは、ウィンドウに送信されたすべてのメッセージを受信して処理する関数です。 すべてのウィンドウ クラスにはウィンドウ プロシージャがあり、そのクラスで作成されたすべてのウィンドウでは、同じウィンドウ プロシージャを使用してメッセージに応答します。
システムは、メッセージ データを引数としてプロシージャに渡すことによって、ウィンドウ プロシージャにメッセージを送信します。 その後、ウィンドウ プロシージャはメッセージに対して適切なアクションを実行します。メッセージ識別子がチェックされ、メッセージの処理中に、メッセージ パラメーターで指定された情報が使用されます。
通常、ウィンドウ プロシージャはメッセージを無視しません。 メッセージを処理しない場合は、既定の処理のためにメッセージをシステムに送り返す必要があります。 ウィンドウ プロシージャは、既定のアクションを実行し、メッセージの結果を返す DefWindowProc 関数を呼び出すことによってこれを行います。 その後、ウィンドウ プロシージャは、この値を独自のメッセージ結果として返す必要があります。 ほとんどのウィンドウ プロシージャでは、少数のメッセージのみを処理し、 DefWindowProc を呼び出して他のメッセージをシステムに渡します。
ウィンドウ プロシージャは同じクラスに属するすべてのウィンドウで共有されるため、複数の異なるウィンドウのメッセージを処理できます。 メッセージの影響を受ける特定のウィンドウを識別するために、ウィンドウ プロシージャは、メッセージと共に渡されたウィンドウ ハンドルを調べることができます。 ウィンドウ プロシージャの詳細については、「 ウィンドウ プロシージャ」を参照してください。
メッセージのフィルター処理
アプリケーションでは、 GetMessage または PeekMessage 関数を使用してメッセージ フィルターを指定することで、メッセージ キューから取得する特定のメッセージを選択できます (他のメッセージは無視されます)。 フィルターは、メッセージ識別子の範囲 (最初と最後の識別子で指定)、ウィンドウ ハンドル、またはその両方です。 GetMessage と PeekMessage では 、メッセージ フィルターを使用して、キューから取得するメッセージを選択します。 メッセージ フィルター処理は、アプリケーションがメッセージ キューを検索して、後でキューに到着したメッセージを検索する必要がある場合に便利です。 また、アプリケーションが投稿されたメッセージを処理する前に入力 (ハードウェア) メッセージを処理する必要がある場合にも役立ちます。
WM_KEYFIRST定数とWM_KEYLAST定数をフィルター値として使用して、すべてのキーボード メッセージを取得できます。WM_MOUSEFIRST定数とWM_MOUSELAST定数を使用して、すべてのマウス メッセージを取得できます。
メッセージをフィルター処理するアプリケーションでは、メッセージ フィルターを満たすメッセージを投稿できるようにする必要があります。 たとえば、アプリケーションがキーボード入力を受け取らないウィンドウで WM_CHAR メッセージをフィルター処理した場合、 GetMessage 関数は返しません。 これにより、アプリケーションが実質的に "ハング" します。
メッセージの投稿と送信
どのアプリケーションでも、メッセージを投稿および送信できます。 システムと同様に、アプリケーションはメッセージをメッセージ キューにコピーしてメッセージを投稿し、メッセージ データを引数としてウィンドウ プロシージャに渡すことによってメッセージを送信します。 メッセージを投稿するために、アプリケーションは PostMessage 関数を使用します。 アプリケーションは、 SendMessage、 BroadcastSystemMessage、 SendMessageCallback、 SendMessageTimeout、 SendNotifyMessage、または SendDlgItemMessage 関数を呼び出すことによってメッセージを送信できます。
メッセージの投稿
アプリケーションは通常、タスクを実行するように特定のウィンドウに通知するメッセージを投稿します。 PostMessage は メッセージの MSG 構造体を作成し、メッセージをメッセージ キューにコピーします。 アプリケーションのメッセージ ループは、最終的にメッセージを取得し、適切なウィンドウ プロシージャにディスパッチします。
アプリケーションは、ウィンドウを指定せずにメッセージを投稿できます。 アプリケーションが PostMessage を呼び出すときに NULL ウィンドウ ハンドルを提供する場合、メッセージは現在のスレッドに関連付けられているキューにポストされます。 ウィンドウ ハンドルが指定されていないため、アプリケーションはメッセージ ループでメッセージを処理する必要があります。 これは、特定のウィンドウではなく、アプリケーション全体に適用されるメッセージを作成する 1 つの方法です。
場合によっては、システム内のすべての最上位ウィンドウにメッセージを投稿することがあります。 アプリケーションは、PostMessage を呼び出し、hwnd パラメーターで HWND_TOPMOSTを指定することで、すべての最上位ウィンドウにメッセージを投稿できます。
一般的なプログラミング エラーは、 PostMessage 関数が常にメッセージを投稿することを想定することです。 これは、メッセージ キューがいっぱいの場合は当てはまらない。 アプリケーションは、PostMessage 関数の戻り値をチェックして、メッセージが投稿されているかどうかを判断し、投稿していない場合は再投稿する必要があります。
sending messages
通常、アプリケーションは、タスクを直ちに実行するようにウィンドウ プロシージャに通知するメッセージを送信します。 SendMessage 関数は、指定されたウィンドウに対応するウィンドウ プロシージャにメッセージを送信します。 この関数は、ウィンドウ プロシージャの処理が完了するまで待機し、メッセージの結果を返します。 親ウィンドウと子ウィンドウは、多くの場合、相互にメッセージを送信して通信します。 たとえば、編集コントロールを子ウィンドウとして持つ親ウィンドウでは、メッセージを送信してコントロールのテキストを設定できます。 コントロールは、親ウィンドウにメッセージを送り返すことによって、ユーザーが実行するテキストに対する変更を親ウィンドウに通知できます。
SendMessageCallback 関数は、指定されたウィンドウに対応するウィンドウ プロシージャにもメッセージを送信します。 ただし、この関数は直ちにを返します。 ウィンドウ プロシージャがメッセージを処理した後、システムは指定されたコールバック関数を呼び出します。 コールバック関数の詳細については、 SendAsyncProc 関数を参照してください。
場合によっては、システム内のすべての最上位ウィンドウにメッセージを送信することが必要になる場合があります。 たとえば、アプリケーションがシステム時刻を変更する場合は、WM_TIMECHANGE メッセージを送信して、その変更についてすべての最上位ウィンドウ に 通知する必要があります。 アプリケーションは、SendMessage を呼び出し、hwnd パラメーターで HWND_TOPMOSTを指定することで、すべての最上位ウィンドウにメッセージを送信できます。 BroadcastSystemMessage 関数を呼び出し、lpdwRecipients パラメーターで BSM_APPLICATIONSを指定することで、すべてのアプリケーションにメッセージをブロードキャストすることもできます。
ウィンドウ プロシージャは 、InSendMessage 関数または InSendMessageEx 関数を使用して、別のスレッドから送信されたメッセージを処理しているかどうかを判断できます。 この機能は、メッセージ処理がメッセージの配信元に依存する場合に便利です。
メッセージ デッドロック
SendMessage 関数を呼び出して別のスレッドにメッセージを送信するスレッドは、メッセージを受信するウィンドウ プロシージャが戻るまで実行を続行できません。 メッセージの処理中に受信側のスレッドが制御を生成する場合、 SendMessage が返されるのを待機しているため、送信スレッドの実行を続行できません。 受信側のスレッドが送信側と同じキューに接続されている場合は、アプリケーションのデッドロックが発生する可能性があります。 (ジャーナル フックはスレッドを同じキューにアタッチします。
受信スレッドは明示的に制御を生成する必要はありません。次のいずれかの関数を呼び出すと、スレッドが暗黙的に制御を生成する可能性があります。
- ダイアログ
- DialogBoxIndirect
- DialogBoxIndirectParam
- DialogBoxParam
- GetMessage
- メッセージ ボックス
- PeekMessage
- SendMessage
アプリケーションでデッドロックが発生する可能性を回避するには、 SendNotifyMessage 関数または SendMessageTimeout 関数の使用を検討してください。 それ以外の場合、ウィンドウ プロシージャは、受信したメッセージが InSendMessage または InSendMessageEx 関数を呼び出すことによって、別のスレッドによって送信されたかどうかを判断できます。 メッセージの処理中に前の一覧のいずれかの関数を呼び出す前に、ウィンドウ プロシージャは最初 に InSendMessage または InSendMessageEx を呼び出す必要があります。 この関数が TRUE を返す場合、スレッドが制御を生成する関数の前に、ウィンドウ プロシージャで ReplyMessage 関数を呼び出す必要があります。
メッセージのブロードキャスト
各メッセージは、メッセージ識別子と、 wParam と lParam の 2 つのパラメーターで構成されます。 メッセージ識別子は、メッセージの目的を指定する一意の値です。 パラメーターはメッセージ固有の追加情報を提供しますが、 wParam パラメーターは通常、メッセージに関する詳細情報を提供する型値です。
メッセージ ブロードキャストは、単にシステム内の複数の受信者にメッセージを送信することです。 アプリケーションからメッセージをブロードキャストするには、 BroadcastSystemMessage 関数を使用して、メッセージの受信者を指定します。 個々の受信者を指定するのではなく、1 つ以上の種類の受信者を指定する必要があります。 これらの種類は、アプリケーション、インストール可能なドライバー、ネットワーク ドライバー、システム レベルのデバイス ドライバーです。 システムは、指定された各型のすべてのメンバーにブロードキャスト メッセージを送信します。
システムは通常、システム レベルのデバイス ドライバーまたは関連コンポーネント内で行われた変更に応じてメッセージをブロードキャストします。 ドライバーまたは関連コンポーネントは、変更を通知するために、アプリケーションやその他のコンポーネントにメッセージをブロードキャストします。 たとえば、ディスク ドライブを担当するコンポーネントは、フロッピー ディスク ドライブのデバイス ドライバーが、ユーザーがドライブにディスクを挿入したときなどのメディアの変更を検出するたびに、メッセージをブロードキャストします。
システムは、システム レベルのデバイス ドライバー、ネットワーク ドライバー、インストール可能なドライバー、アプリケーションの順に受信者にメッセージをブロードキャストします。 つまり、システム レベルのデバイス ドライバーが受信者として選択されている場合、常にメッセージに応答する最初の機会が得られます。 特定の受信者の種類では、他のドライバーの前に特定のメッセージを受信するドライバーは保証されません。 つまり、特定のドライバーを対象としたメッセージには、他のドライバーが意図せずに処理しないように、グローバルに一意のメッセージ識別子が必要です。
SendMessage、SendMessageCallback、SendMessageTimeout、または SendNotifyMessage 関数でHWND_BROADCASTを指定して、すべての最上位ウィンドウにメッセージをブロードキャストすることもできます。
アプリケーションは、最上位ウィンドウのウィンドウ プロシージャを通じてメッセージを受信します。 メッセージは子ウィンドウに送信されません。 サービスは、ウィンドウ プロシージャまたはそのサービス コントロール ハンドラーを介してメッセージを受信できます。
Note
システム レベルのデバイス ドライバーは、関連するシステム レベルの関数を使用してシステム メッセージをブロードキャストします。
メッセージのクエリ
独自のカスタム メッセージを作成し、それらを使用して、アプリケーションとシステム内の他のコンポーネント間のアクティビティを調整できます。 これは、独自のインストール可能なドライバーまたはシステム レベルのデバイス ドライバーを作成した場合に特に便利です。 カスタム メッセージは、ドライバーとドライバーを使用するアプリケーションとの間で情報を伝達できます。
特定のアクションを実行するアクセス許可を受信者にポーリングするには、 クエリ メッセージを使用します。 BroadcastSystemMessage を呼び出すときに dwFlags パラメーターにBSF_QUERY値を設定することで、独自のクエリ メッセージを生成できます。 クエリ メッセージの各受信者は、関数がメッセージを次の受信者に送信するために TRUE を 返す必要があります。 受信者が BROADCAST_QUERY_DENYを返した場合、ブロードキャストはすぐに終了し、関数は 0 を返します。