在this thread(大约一年前发布)中,讨论了在非交互式会话中运行Word时可能遇到的问题。那里给出的(非常强烈的)建议不会这样做。在一篇文章中,它表示“Office API都假设您在桌面上的交互式会话中运行Office,具有监视器,键盘和鼠标,最重要的是消息泵。”我不确定那是什么。 (我已经用C#编程了大约一年;我的其他编程经验主要是使用ColdFusion。)
我的程序运行大量RTF文件,以提取用于构建医疗报告编号的两条信息。我没有尝试弄清楚RTF中的格式化指令是如何工作的,而是决定只在Word中打开它们并从那里拉出文本(而不是实际启动GUI)。有时候,程序在处理一个文件的过程中会打嗝,并且会打开一个附加到该文档的Word线程(我仍然需要弄清楚如何关闭那个文件)。当我重新运行程序时,我当然得到一个通知,说有一个线程正在使用该文件,我是否想要打开一个只读副本?当我说“是”时,Word GUI突然突然冒出来并开始处理文件。我想知道为什么会这样;但看起来可能一旦弹出对话框,消息泵开始将主GUI推送到Windows?
答案 0 :(得分:164)
消息循环是存在于任何本机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 coclass处理线程,它确保在COM接口上进行的调用始终来自正确的线程。大多数COM类在注册表中都有一个注册表项,用于声明其ThreadingModel,到目前为止最常见的(包括Office)使用“Apartment”。这意味着调用接口方法的唯一安全方法是从创建类对象的同一线程进行调用。或者换句话说:到目前为止,大多数COM类都不是线程安全的。
每个启用COM的线程都属于COM公寓。有单线程公寓(STA)和多线程公寓(MTA)两种。必须在STA线程上创建一个公寓线程COM类。您可以在.NET程序中看到这一点,Windows窗体或WPF程序的UI线程的入口点具有[STAThread]属性。其他线程的单元模型由Thread.SetApartmentState()方法设置。
如果UI线程不是STA,则Windows管道的大部分将无法正常工作。特别是Drag + Drop,剪贴板,Windows对话框,如OpenFileDialog,控件如WebBrowser,UI自动化应用程序,如屏幕阅读器。还有很多COM服务器,比如Office。
STA线程的一个硬性要求是它永远不应该阻塞并且必须泵送消息循环。消息循环很重要,因为这是COM用于编组从一个线程到另一个线程的接口方法调用的内容。尽管.NET使编组调用变得容易(例如Control.BeginInvoke或Dispatcher.BeginInvoke),但它实际上是一件非常棘手的事情。执行调用的线程必须处于众所周知的状态。你不能随意中断一个线程并强制它进行方法调用,这会导致可怕的重入问题。线程应该是“空闲”,而不是忙于执行任何改变程序状态的代码。
也许你可以看到领先的地方:是的,当程序执行消息循环时,它是空闲的。实际的编组通过COM创建的隐藏窗口进行,它使用PostMessage使该窗口的窗口过程执行代码。在STA线程上。消息循环确保此代码运行。
答案 1 :(得分:10)
“消息泵”是任何Windows程序的核心部分,负责将窗口消息分派给应用程序的各个部分。这是Win32 UI编程的核心。由于它无处不在,许多应用程序使用消息泵在不同模块之间传递消息,这就是Office应用程序在没有任何UI的情况下运行时会中断的原因。
维基百科有basic description。
答案 2 :(得分:6)
John正在讨论Windows系统(以及其他基于窗口的系统 - X Window,原始Mac OS ....)如何通过消息系统使用事件实现异步用户界面。
每个应用程序的幕后都有一个消息传递系统,每个窗口都可以将事件发送到其他窗口或事件侦听器 - 这是通过向消息队列添加消息来实现的。有一个主循环,它始终运行查看此消息队列,然后将消息(或事件)分派给侦听器。
维基百科文章 Message loop in Microsoft Windows 显示了基本Windows程序的示例代码 - 正如您在最基本的级别所看到的,Windows程序只是“消息泵”。 / p>
所以,把它们全部拉到一起。设计用于支持UI的Windows程序无法充当服务的原因是因为它需要始终运行消息循环以启用UI支持。如果您将其实现为所描述的服务,它将无法处理内部异步事件处理。
答案 3 :(得分:5)
在COM中,消息泵对公寓之间发送的消息进行串行化和反序列化。公寓是一个可以运行COM组件的迷你过程。公寓采用单线程和免费线程模式。单线程公寓主要是遗留系统,用于不支持多线程的COM组件的应用。它们通常与Visual BASIC(因为它不支持多线程代码)和遗留应用程序一起使用。
我想Word的消息泵需求源于COM API或应用程序的部分不是线程安全的。请记住,.NET线程和垃圾收集模型不能很好地与COM开箱即用。 COM有一个非常简单的垃圾收集机制和线程模型,它要求你以COM的方式做事。使用标准Office PIAs仍然需要您显式关闭COM对象引用,因此您需要跟踪创建的每个COM句柄。如果你不小心,PIA也会在幕后制作东西。
.NET-COM集成本身就是一个完整的主题,甚至还有关于这个主题的书籍。即使从交互式桌面应用程序使用用于Office的COM API,也需要跳过箍并确保明确发布引用。
可以假定Office是线程不安全的,因此每个线程都需要单独的Word,Excel或其他Office应用程序实例。您必须承担起始开销或维护线程池。必须仔细测试线程池以确保正确释放所有COM引用。即使启动和关闭实例,也需要确保正确释放所有引用。如果没有点你的i并在这里交叉,将导致大量死COM对象,甚至整个运行的Word实例都被泄露。
答案 4 :(得分:2)
维基百科建议它意味着该计划的main Event Loop。
答案 5 :(得分:0)
我认为this Channel 9 discussion有一个很简洁的解释:
这种窗口通信过程可以通过所谓的Windows消息泵实现。将Message Pump视为一个实现应用程序窗口和桌面之间协作的实体。