在 Win32 和 WPF 之间共享消息循环

本主题介绍如何实现与 Windows Presentation Foundation(WPF)的互操作,可以通过使用 Dispatcher 中现有的消息循环,或者在互操作代码的 Win32 端创建一个单独的消息循环来实现。

ComponentDispatcher 和 消息循环

在互操作和键盘事件支持中,正常方案包括实现 IKeyboardInputSink 或从已实现 IKeyboardInputSink 的类(例如 HwndSourceHwndHost)中实现子类。 但是,键盘接收器支持不能解决在跨互作边界发送和接收消息时可能需要的所有消息循环需求。 为了帮助正式化应用程序消息循环体系结构,Windows Presentation Foundation (WPF) 提供了一 ComponentDispatcher 个类,该类定义要遵循的消息循环的简单协议。

ComponentDispatcher 是公开多个成员的静态类。 每个方法的范围都隐式绑定到调用线程。 消息循环必须在关键时间调用其中一些 API(在下一部分定义)。

ComponentDispatcher 提供其他组件(如键盘侦听器)可以监听的事件。 该 Dispatcher 类以适当的顺序调用所有适当的 ComponentDispatcher 方法。 如果要实现自己的消息循环,则代码负责以类似的方式调用 ComponentDispatcher 方法。

在线程上调用 ComponentDispatcher 方法只会调用在该线程上注册的事件处理程序。

编写消息循环

下面是编写自己的消息循环时将使用的成员清单:ComponentDispatcher

使用 ComponentDispatcher 和现有消息处理

下面是如果您依赖固有 WPF 消息循环时将会使用的ComponentDispatcher成员清单。

如果在ThreadFilterMessage事件或ThreadPreprocessMessage事件之后,事件数据中引用传递的参数handledtrue,则消息被认为已处理。 事件处理程序应忽略消息(如果 handledtrue),因为这意味着不同的处理程序首先处理了消息。 这两个事件的事件处理程序可以修改消息。 调度程序应调度修改的消息,而不是原始未更改的消息。 ThreadPreprocessMessage 会传递给所有侦听器,但体系结构的意图是,只有包含目标 HWND 的顶级窗口应该调用代码以响应这些消息。

HwndSource 如何处理 ComponentDispatcher 事件

如果 HwndSource 是一个顶级窗口(没有父 HWND),它将注册到 ComponentDispatcher。 如果ThreadPreprocessMessage触发,并且消息目标是HwndSource或其子窗口,HwndSource会调用其IKeyboardInputSink.TranslateAcceleratorTranslateCharOnMnemonic键盘接收器序列。

如果HwndSource不是顶级窗口(即有父HWND),则不会被处理。 只有顶级窗口负责处理,并且在任何交互操作中,预计会有一个支持键盘输入处理的顶级窗口。

在未首先为 WndProc 调用适当的键盘接收器方法的情况下,如果在 HwndSource 上被调用时,您的应用程序将会接收到更高级别的键盘事件,例如 KeyDown。 但是,不会调用任何键盘接收器方法,从而规避所需的键盘输入模型功能,例如访问键支持。 这可能是因为消息循环未正确通知ComponentDispatcher上的相关线程,或者父窗口句柄未调用适当的键盘响应。

如果您使用AddHook方法为该消息添加挂钩,则发往键盘接收器的消息可能不会发送到HWND。 消息可能在消息泵级别被直接处理,而未提交到 DispatchMessage 函数。

另请参阅