什么是信息泵?

这根线(大约一年前发布的)中有一个关于在非交互式会话中运行 Word 可能带来的问题的讨论。给出的(相当强烈的)建议是不要这样做。在一篇文章中写道: “ Office API 都假设您在桌面上以交互式会话方式运行 Office,并配有显示器、键盘和鼠标,最重要的是,还有一个消息泵。”我不确定那是什么。(我用 C # 编程只有一年的时间; 我的其他编程经验主要来自 ColdFusion。)

更新:

我的程序运行通过大量的 RTF 文件提取两个信息用于构造一个医疗报告编号。我没有尝试去弄清楚 RTF 中的格式化指令是如何工作的,而是决定直接在 Word 中打开它们,然后从中抽出文本(实际上并没有启动 GUI)。有时候,程序在处理一个文件的过程中会出现问题,并且留下一个连接到该文件的 Word 线程处于打开状态(我仍然需要弄清楚如何关闭那个线程)。当我重新运行该程序时,当然我收到了一个通知,说有一个线程正在使用该文件,我是否想打开一个只读副本?当我说 Yes 的时候,Word GUI 突然不知从哪里冒出来,开始处理文件。我想知道为什么会发生这种情况; 但是看起来好像一旦对话框弹出,消息泵也开始将主 GUI 推向 Windows?

58525 次浏览

维基百科认为这意味着程序的 主事件回路

“消息泵”是任何 Windows 程序的核心部分,它负责将窗口消息发送到应用程序的各个部分。这是 Win32UI 编程的核心。由于它的普遍性,许多应用程序使用消息泵在不同的模块之间传递消息,这就是为什么 Office 应用程序如果在没有任何 UI 的情况下运行会中断的原因。

维基百科有 基本描述

John 正在谈论 Windows 系统(以及其他基于窗口的系统—— X 窗口,原始的 Mac OS... .)如何通过消息系统使用事件实现异步用户界面。

每个应用程序的幕后都有一个消息传递系统,其中每个窗口都可以向其他窗口或事件侦听器发送事件——这是通过向消息队列添加消息来实现的。有一个主循环,它总是查看这个消息队列,然后将消息(或事件)分派给侦听器。

Wikipedia 文章 译自: http://www.wikipedia.org/wiki/Message _ loop _ in _ Microsoft _ windows (微软视窗))微软视窗的讯息回圈:展示了一个基本 Windows 程序的示例代码——正如你所看到的,在最基本的层面上,Windows 程序只是一个“消息泵”。

所以,为了把这一切联系起来。为支持 UI 而设计的 Windows 程序之所以不能充当服务,是因为它需要始终运行的消息循环来启用 UI 支持。如果按照描述的方式将其实现为服务,那么它将无法处理内部异步事件处理。

我认为 this Channel 9 discussion有一个很简洁的解释:

这个窗口通信的过程是由所谓的 Windows 消息泵实现的。可以将消息泵看作一个实体,它支持应用程序窗口和桌面之间的协作。

消息循环是存在于任何本机 Windows 程序中的一小段代码。它大致是这样的:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

GetMessage () Win32API 从 Windows 检索消息。你的程序通常会花费99.9% 的时间在那里,等待 Windows 告诉它发生了一些有趣的事情。TransateMessage ()是一个帮助函数,用于转换键盘消息。DispatchMessage ()确保使用消息调用窗口过程。

每个启用了 GUI 的.NET 程序都有一个消息循环,它由 Application.Run ()启动。

消息循环与 Office 的相关性与 COM 相关。Office 程序是支持 COM 的程序,这就是微软。办公室。互操课很管用。COM 代表 COM 辅助类处理线程,它确保对 COM 接口的调用始终从正确的线程发出。大多数 COM 类在声明其 ThreadingModel 的注册表中都有一个注册表项,到目前为止最常见的类(包括 Office)使用“公寓”。这意味着调用接口方法的唯一安全方法是从创建类对象的同一线程进行调用。或者换句话说: 到目前为止,大多数 COM 类都不是线程安全的。

每个启用了 COM 的线程都属于一个 COM 单元。有两种,单线程公寓(STA)和多线程公寓(MTA)。必须在 STA 线程上创建单元线程 COM 类。你可以把这个放回去。NET 程序中,Windows 窗体或 WPF 程序的 UI 线程的入口点具有[ STAThread ]属性。其他线程的单元模型由 Thread 设置。SetApartmentState ()方法。

如果 UI 线程不是 STA,Windows 管道的大部分工作将不能正常工作。特别是拖放,剪贴板,Windows 对话框如 OpenFileDialog,控件如 WebBrowser,UI 自动化应用程序如屏幕阅读器。和许多 COM 服务器,如 Office。

STA 线程的一个硬性要求是它永远不应该阻塞并且必须泵送消息循环。消息循环很重要,因为 COM 使用它来将接口方法调用从一个线程封送到另一个线程。虽然。NET 使封送调用变得容易(控件。BeginInvoke 或 Dispatcher。例如 BeginInvoke) ,这实际上是一件非常棘手的事情。执行调用的线程必须处于已知状态。您不能随意中断一个线程并强制它进行方法调用,这会导致可怕的重入问题。线程应该是“空闲”的,不要忙于执行任何正在改变程序状态的代码。

也许您可以看到这样的结果: 是的,当程序执行消息循环时,它是空闲的。实际的封送处理通过 COM 创建的隐藏窗口进行,它使用 PostMessage 让该窗口的窗口过程执行代码。在 STA 的帖子里。消息循环确保此代码运行。

COM中,消息泵对公寓之间发送的消息进行序列化和反序列化。单元是一个可以运行 COM 组件的迷你进程。公寓有单线程和自由线程模式。单线程单元主要是不支持多线程的 COM 组件应用程序的遗留系统。它们通常与 VisualBASIC (因为它不支持多线程代码)和遗留应用程序一起使用。

我猜测 没错的消息泵需求源于 COM API 或应用程序的某些部分不是线程安全的。请记住,.NET线程和垃圾收集模型不能很好地使用 COM 开箱即用。COM 有一个非常简单的垃圾收集机制和线程模型,需要您按 COM 的方式来做事情。使用标准 Office PIAs仍然需要显式关闭 COM 对象引用,因此需要跟踪创建的每个 COM 句柄。如果你不小心的话,PIA 也会在幕后制造东西。

.NET-COM 集成本身就是一个完整的主题,甚至还有相关的书籍。即使在交互式桌面应用程序中使用用于 Office 的 COMAPI,也需要您跳过一些障碍,并确保显式释放引用。

Office 可能被认为是线程不安全的,因此对于每个线程,您将需要一个单独的 Word、 Excel或其他 办公室应用程序实例。您将不得不承担启动开销或维护线程池。必须仔细测试线程池,以确保所有 COM 引用都被正确释放。即使启动和关闭实例也需要您确保正确释放所有引用。在这里,如果不能点对你的 i 和 t,将会导致大量的死 COM 对象,甚至整个正在运行的 Word 实例被泄漏。