消息循环是任何本机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线程上。消息循环确保此代码运行。