什么是消息泵?


103

此线程(大约一年前发布)中,讨论了在非交互式会话中运行Word可能带来的问题。给出的(非常强烈的)建议不这样做。在一篇文章中说:“所有Office API都假设您正在桌面上的交互式会话中运行Office,并带有监视器,键盘和鼠标,最重要的是,消息泵。” 我不确定那是什么。(我使用C#编程仅一年左右;我的其他编程经验主要是使用ColdFusion。)

更新:

我的程序通过大量的RTF文件运行,以提取用于构造医疗报告编号的两条信息。我没有尝试弄清楚RTF中的格式化指令如何工作,而是决定只在Word中打开它们,然后从那里拉出文本(实际上没有启动GUI)。有时,该程序在处理一个文件的过程中出现了打ic,并在该文件上保留了一个Word线程打开(我仍然必须弄清楚如何关闭该文件)。当我重新运行该程序时,我当然会收到一条通知,指出有一个线程正在使用该文件,我是否想打开一个只读副本?当我说“是”时,Word GUI突然从任何地方弹出并开始处理文件。我想知道为什么会这样。


3
为什么将其标记为win32?-信息系统是在Windows V1(其中,我记得是8位。)
霍根

Answers:


186

消息循环是任何本机Windows程序中都存在的一小段代码。大致如下所示:

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

GetMessage()Win32 API从Windows检索消息。您的程序通常在该处花费99.9%的时间,等待Windows告诉它发生了一些有趣的事情。TranslateMessage()是转换键盘消息的辅助函数。DispatchMessage()确保通过消息调用窗口过程。

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

消息循环与Office的相关性与COM有关。Office程序是启用COM的程序,这就是Microsoft.Office.Interop类的工作方式。COM代表COM协类处理线程,它确保在COM接口上进行的调用始终是从正确的线程进行的。大多数COM类在注册表中都有一个注册表项,用于声明其ThreadingModel,到目前为止,最常见的类(包括Office)使用“公寓”。这意味着调用接口方法的唯一安全方法是通过与创建类对象相同的线程进行调用。或者换一种说法:到目前为止,大多数COM类都不是线程安全的。

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

如果UI线程不是STA,则Windows管道的大部分将无法正常工作。尤其是Drag + Drop,剪贴板,Windows对话框(如OpenFileDialog),控件(如WebBrowser),UI Automation应用程序(如屏幕阅读器)。还有许多COM服务器,例如Office。

对STA线程的一个严格要求是,它永远都不应阻塞并且必须泵送消息循环。消息循环很重要,因为这就是COM用来封送从一个线程到另一个线程的接口方法调用的方式。尽管.NET使封送处理调用变得容易(例如Control.BeginInvoke或Dispatcher.BeginInvoke),但这实际上是一件非常棘手的事情。执行调用的线程必须处于众所周知的状态。您不能随便中断线程并强迫它进行方法调用,这将导致可怕的重新输入问题。线程应为“空闲”,而不是忙于执行任何使程序状态发生变化的代码。

也许您会看到导致的结果:是的,当程序执行消息循环时,它处于空闲状态。实际的封送处理是通过COM创建的隐藏窗口进行的,它使用PostMessage使该窗口的窗口过程执行代码。在STA线程上。消息循环确保此代码运行。


非常好的和详细的答案。只需添加-还有一个称为主STA的特殊STA,这是创建的第一个STA。理想情况下,应该是您的UI线程创建的那个。主STA是在其中创建线程模型=无组件的组件。如果主STA不是由UI线程创建的组件,则在使用没有线程模型的较旧的Activex控件时,您可能会遇到有趣的问题。
quixver 2014年

12

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

维基百科具有基本描述


我相信没有消息循环就不可能编写Windows应用程序,因此所有应用程序都使用消息泵。
霍根2010年

2
您也可以编写简单的GUI应用程序而无需编写一个-例如,您可以弹出消息框,而自己的应用程序中没有消息循环。

如果通过DialogBox或DialogBox间接创建对话框-不需要消息循环,则只需提供将由Windows调用的函数(dlgproc)。(消息框只是一个简单的对话框)
quixver 2014年

6

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

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

Wikipedia文章Microsoft Windows中的“ 消息循环”显示了基本Windows程序的示例代码-并且,如您在最基本的级别上看到的,Windows程序只是“消息泵”。

因此,将其组合在一起。设计用于支持UI的Windows程序不能充当服务的原因是,它需要始终运行的消息循环才能启用UI支持。如果按照说明将其实现为服务,则它将无法处理内部异步事件处理。


6

COM中,消息泵对在公寓之间发送的消息进行序列化和反序列化。公寓是一个微型过程,可以在其中运行COM组件。套间有单线程和自由线程模式。单线程单元主要是用于不支持多线程的COM组件应用程序的旧系统。它们通常与Visual BASIC(因为它不支持多线程代码)和旧版应用程序一起使用。

我猜想Word的消息泵要求源于COM API或应用程序的某些部分不是线程安全的。请记住,.NET线程和垃圾回收模型不能很好地与COM配合使用。COM具有非常简单的垃圾收集机制和线程模型,需要您以COM方式进行操作。使用标准Office PIA仍然需要您显式关闭COM对象引用,因此您需要跟踪创建的每个COM句柄。如果您不小心,PIA还会在后台创建东西。

.NET-COM集成本身就是一个完整的主题,甚至有关于该主题的书籍。即使从交互式桌面应用程序中使用Office的COM API,也需要您跳过圈并确保明确释放引用。

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


1
您的答案有一些错误。公寓共有3种类型-STA(单线程),MTA(多线程)和NTA(中性线程)。自由线程用于描述聚集自由线程编组器的组件,没有自由线程单元之类的东西。COM使用消息与STA进行通信。对于MTA中的组件(或聚集了自由线程编组器的组件),不需要消息循环。AFAIK-lpc用于将数据从调用线程整理到RPC线程池中的线程,然后RPC线程池实际调用该方法。
quixver 2014年


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.