本主题介绍如何实现与 Windows Presentation Foundation(WPF)的互操作,可以通过使用 Dispatcher 中现有的消息循环,或者在互操作代码的 Win32 端创建一个单独的消息循环来实现。
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上的相关线程,或者父窗口句柄未调用适当的键盘响应。
如果您使用AddHook方法为该消息添加挂钩,则发往键盘接收器的消息可能不会发送到HWND。 消息可能在消息泵级别被直接处理,而未提交到 DispatchMessage
函数。