このトピックでは、 Dispatcher で既存のメッセージ ループの公開を使用するか、相互運用コードの Win32 側に別のメッセージ ループを作成することによって、Windows Presentation Foundation (WPF) との相互運用のためのメッセージ ループを実装する方法について説明します。
ComponentDispatcher とメッセージ ループ
相互運用とキーボード イベントサポートの通常のシナリオは、IKeyboardInputSinkを実装するか、IKeyboardInputSinkやHwndSourceなどのHwndHostを既に実装しているクラスからサブクラス化することです。 ただし、キーボード シンクのサポートでは、相互運用境界を越えてメッセージを送受信する際に必要となる可能性のあるすべてのメッセージ ループのニーズに対応できるわけではありません。 Windows Presentation Foundation (WPF) は、アプリケーション メッセージ ループ アーキテクチャを形式化するために、 ComponentDispatcher クラスを提供します。このクラスは、メッセージ ループが従う単純なプロトコルを定義します。
ComponentDispatcher は、複数のメンバーを公開する静的クラスです。 各メソッドのスコープは、呼び出し元のスレッドに暗黙的に関連付けられます。 メッセージ ループは、(次のセクションで定義されているように) 重要な時間にこれらの API の一部を呼び出す必要があります。
ComponentDispatcher は、他のコンポーネント (キーボードシンクなど) が受け取ることができるイベントを提供します。 Dispatcher クラスは、適切なシーケンス内のすべての適切なComponentDispatcher メソッドを呼び出します。 独自のメッセージ ループを実装する場合、コードは同様の方法で ComponentDispatcher メソッドを呼び出す役割を担います。
スレッド ComponentDispatcher メソッドを呼び出すと、そのスレッドに登録されたイベント ハンドラーのみが呼び出されます。
メッセージ ループの書き込み
独自のメッセージ ループを記述する場合に使用 ComponentDispatcher メンバーのチェックリストを次に示します。
PushModal: メッセージ ループはこれを呼び出して、スレッドがモーダルであることを示す必要があります。
PopModal:メッセージ ループでこれを呼び出して、スレッドが非変更に戻されたことを示す必要があります。
RaiseIdle: メッセージ ループはこれを呼び出して、 ComponentDispatcher が ThreadIdle イベントを発生させる必要があることを示す必要があります。 ComponentDispatcherは、ThreadIdleがIsThreadModal状態の場合でも
true
を発生させませんが、モーダル状態でRaiseIdleが応答できなくても、メッセージループはComponentDispatcherを呼び出すことができます。RaiseThreadMessage: メッセージ ループでこれを呼び出して、新しいメッセージが使用可能であることを示す必要があります。 戻り値は、 ComponentDispatcher イベントのリスナーがメッセージを処理したかどうかを示します。 RaiseThreadMessageが
true
(処理済み)を返す場合、ディスパッチャーはメッセージに対して何もする必要はありません。 戻り値がfalse
場合、ディスパッチャーは Win32 関数TranslateMessage
を呼び出し、DispatchMessage
を呼び出す必要があります。
ComponentDispatcher と既存のメッセージ処理の使用
固有の WPF メッセージ ループに依存する場合に使用 ComponentDispatcher メンバーのチェックリストを次に示します。
IsThreadModal: アプリケーションがモーダルになったかどうかを返します (たとえば、モーダル メッセージ ループがプッシュされました)。 ComponentDispatcher は、クラスがメッセージ ループからの PushModal および PopModal 呼び出しの数を保持するため、この状態を追跡できます。
ThreadFilterMessage および ThreadPreprocessMessage イベントは、デリゲート呼び出しの標準規則に従います。 デリゲートは指定されていない順序で呼び出され、最初のデリゲートがメッセージを処理済みとしてマークした場合でも、すべてのデリゲートが呼び出されます。
ThreadIdle: アイドル処理を実行するための適切で効率的な時間を示します (スレッドの保留中のメッセージは他にありません)。 ThreadIdle スレッドがモーダルの場合は発生しません。
ThreadFilterMessage: メッセージ ポンプが処理するすべてのメッセージに対して発生します。
ThreadPreprocessMessage: ThreadFilterMessage中に処理されなかったすべてのメッセージに対して発生します。
メッセージは、 ThreadFilterMessage イベントまたは ThreadPreprocessMessage イベントの後に、イベント データで参照渡しされる handled
パラメーターが true
された場合に処理されると見なされます。
handled
がtrue
されている場合、イベント ハンドラーはメッセージを無視する必要があります。これは、別のハンドラーが最初にメッセージを処理したことを意味するためです。 両方のイベントのイベント ハンドラーによってメッセージが変更される場合があります。 ディスパッチャーは、変更されていない元のメッセージではなく、変更されたメッセージをディスパッチする必要があります。
ThreadPreprocessMessage はすべてのリスナーに配信されますが、アーキテクチャ上の目的は、対象のメッセージがメッセージに応答してコードを呼び出す必要がある HWND を含む最上位ウィンドウのみです。
HwndSource で ComponentDispatcher イベントを処理する方法
HwndSourceがトップレベル ウィンドウ (親 HWND なし) の場合は、ComponentDispatcherに登録されます。 ThreadPreprocessMessageが発生し、メッセージがHwndSourceウィンドウまたは子ウィンドウを対象としている場合、HwndSourceはIKeyboardInputSink.TranslateAccelerator、TranslateChar、OnMnemonicキーボードシンクシーケンスを呼び出します。
HwndSourceが最上位ウィンドウでない場合 (親 HWND を持つ)、処理は行われません。 処理が行われるのは最上位のウィンドウのみであり、相互運用シナリオの一部として、キーボード シンクのサポートを備えた最上位ウィンドウが必要です。
WndProcのHwndSourceが適切なキーボード シンク メソッドを最初に呼び出さずに呼び出された場合、アプリケーションは、KeyDownなどの上位レベルのキーボード イベントを受け取ります。 ただし、キーボード シンク メソッドは呼び出されません。これにより、アクセス キーのサポートなどの望ましいキーボード入力モデル機能が回避されます。 これは、メッセージ ループが ComponentDispatcherの関連するスレッドに適切に通知しなかったか、親 HWND が適切なキーボード シンク応答を呼び出さなかったために発生する可能性があります。
AddHook メソッドを使用してそのメッセージのフックを追加した場合、キーボード シンクに送信されるメッセージが HWND に送信されない可能性があります。 メッセージは、メッセージ ポンプ レベルで直接処理され、 DispatchMessage
関数に送信されていない可能性があります。
こちらも参照ください
.NET Desktop feedback