什么是消息泵?
在这个线程 (大约一年前发布)中,讨论了在非交互式会话中运行Word的问题。 这个(相当强)的build议是不这样做的。 在其中的一篇文章中提到:“Office API都假设你在桌面上的交互式会话中运行Office,具有显示器,键盘和鼠标,最重要的是一个消息泵。 我不确定那是什么 (我已经用C#编写了大约一年;我的其他编程经验主要用于ColdFusion。)
更新:
我的程序通过大量的RTF文件运行,以提取用于构build医疗报告编号的两条信息。 我没有试图弄清楚RTF中的格式化指令是如何工作的,而是决定在Word中打开它们,并从那里拉出文本(而不是真的启动GUI)。 偶尔,程序在处理一个文件的过程中被打断,并且留下了一个附加在该文件上的Word线程(我仍然需要弄清楚如何closures这个文件)。 当我重新运行程序时,当然我得到了一个使用该文件的线程的通知,并且我想打开一个只读副本? 当我说“是”时,Word GUI突然从无处popup,开始处理文件。 我想知道为什么发生这种情况。 但它看起来像也许一旦对话框popup消息泵开始推动主要的graphics用户界面到Windows?
消息循环是本地Windows程序中存在的一小段代码。 它大致看起来像这样:
MSG msg; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
GetMessage()Win32 API从Windows检索消息。 你的程序通常花99.99%的时间在那里,等待Windows告诉它一些有趣的事情发生。 TranslateMessage()是翻译键盘消息的帮助函数。 DispatchMessage()确保窗口过程与消息一起被调用。
每个启用了.NET的程序都有一个消息循环,它由Application.Run()启动。
消息循环与Office的相关性与COM相关。 Office程序是启用了COM的程序,这就是Microsoft.Office.Interop类的工作方式。 COM代表COM coclass处理线程,它确保在COM接口上进行的调用总是由正确的线程来完成的。 大多数COM类在registry中有一个registry键,它声明了他们的ThreadingModel,到目前为止最常用的(包括Office)使用“Apartment”。 这意味着调用接口方法的唯一安全方法是通过创build类对象的同一个线程进行调用。 或换句话说:到目前为止,大多数COM类不是线程安全的。
每个启用COM的线程都属于COM套件。 有两种,单螺纹公寓(STA)和multithreading公寓(MTA)。 必须在STA线程上创build一个公寓线程COM类。 您可以在.NET程序中看到这一点,Windows窗体或WPF程序的UI线程的入口点具有[STAThread]属性。 其他线程的公寓模型由Thread.SetApartmentState()方法设置。
如果UI线程不是STA,则大部分Windowspipe道工程将无法正常工作。 值得注意的是拖放,剪贴板,Windows对话框,如OpenFileDialog。 和任何ActiveX控件和大多数COM服务器,如Office。
STA线程的一个硬要求就是它永远不要阻塞,而且必须抽取一个消息循环。 消息循环非常重要,因为这是COM用于将一个接口方法调用从一个线程编组到另一个线程。 虽然.NET使编组调用变得简单(例如Control.BeginInvoke或Dispatcher.BeginInvoke),但实际上这是非常棘手的事情。 执行调用的线程必须处于已知状态。 你不能任意中断一个线程,并强制它进行一个方法调用,这会导致可怕的重入问题。 一个线程应该是“空闲的”,而不是忙于执行任何改变程序状态的代码。
也许你可以看到引导的地方:是的,当程序执行消息循环时,它是空闲的。 实际的封送通过COM创build的隐藏窗口进行,使用PostMessage使该窗口的窗口过程执行代码。 在STA线程上。 消息循环确保这个代码运行。
“消息泵”是任何Windows程序的核心部分,负责将窗口消息分派到应用程序的各个部分。 这是Win32 UI编程的核心。 由于其普遍性,许多应用程序使用消息泵在不同模块之间传递消息,这就是Office应用程序在没有任何UI的情况下运行时会中断的原因。
维基百科有一个基本的描述 。
John正在谈论Windows系统(以及其他基于窗口的系统–X Window ,原始Mac OS ….)如何通过消息系统实现使用事件的asynchronous用户界面。
每个应用程序的幕后都有一个消息系统,每个窗口都可以将事件发送到其他窗口或事件监听器 – 这是通过向消息队列添加消息来实现的。 有一个主循环总是运行着看这个消息队列,然后把这个消息(或事件)分配给这个监听器。
维基百科文章Microsoft Windows中的消息循环显示了一个基本Windows程序的示例代码 – 正如您在最基本的层面上看到的,Windows程序只是“消息泵”。
所以,把它们放在一起。 Windows程序devise用于支持UI的原因不能充当服务,因为它需要始终运行的消息循环来启用UI支持。 如果按照所述将其作为服务实现,则将无法处理内部asynchronous事件处理。
在COM中 ,消息泵对公寓之间发送的消息进行序列化和反序列化。 公寓是一个可以运行COM组件的小型stream程。 公寓进来单线程和自由线程模式。 单线程单元主要是用于不支持multithreading的COM组件应用的传统系统。 它们通常与Visual BASIC(因为这不支持multithreading代码)和传统应用程序一起使用。
我想Word的消息泵要求来自COM API或应用程序的部分不是线程安全的。 请记住, .NET线程和垃圾收集模型不能很好地与COM开箱即用。 COM有一个非常简单的垃圾收集机制和线程模型,它要求你以COM的方式来做事情。 使用标准的Office PIA仍然需要你明确地closuresCOM对象引用,所以你需要跟踪每个创build的COM句柄。 如果你不小心的话,PIA也会在幕后创build一些东西。
.NET-COM整合本身就是一个整体,甚至还有关于这个主题的书籍。 即使在交互式桌面应用程序中使用COM API for Office,也需要跳过这些环节,并确保引用是明确发布的。
Office可以被认为是线程不安全的,因此您需要为每个线程单独使用Word, Excel或其他Office应用程序的实例。 你将不得不承担起始开销或维护一个线程池。 一个线程池将不得不被仔细testing,以确保所有的COM参考被正确释放。 即使启动和closures实例,也需要确保所有引用都正确释放。 未能点击我的,并跨越你的t在这里将导致大量死COM对象,甚至整个正在运行的Word实例泄漏。
维基百科build议这意味着该计划的主要事件循环 。
我认为这个第9频道的讨论有一个很好的简洁的解释:
窗口通信的这个过程由所谓的Windows消息泵实现。 将消息泵看作是能够在应用程序窗口和桌面之间进行合作的实体。