Mat*_*ing 95 c# winapi message-pump winmain
在这个帖子(大约一年前发布)中,讨论了在非交互式会话中运行Word时可能遇到的问题.那里给出的(非常强烈的)建议不会这样做.在一篇文章中,它表示"Office API都假设您在桌面上的交互式会话中运行Office,具有监视器,键盘和鼠标,最重要的是消息泵." 我不确定那是什么.(我用C#编程只用了一年左右;我的其他编程经验主要是使用ColdFusion.)
我的程序运行大量RTF文件,以提取用于构建医疗报告编号的两条信息.我没有尝试弄清楚RTF中的格式化指令是如何工作的,而是决定只在Word中打开它们并从那里拉出文本(而不是实际启动GUI).有时,程序在处理一个文件的中间打嗝,并留下了一个字线打开该文件附有(我仍然要弄清楚如何关闭一个向下).当我重新运行程序时,我当然得到一个通知,说有一个线程正在使用该文件,我是否想要打开一个只读副本?当我说"是"时,Word GUI突然突然冒出来并开始处理文件.我想知道为什么会这样; 但看起来可能一旦弹出对话框,消息泵开始将主GUI推送到Windows?
Han*_*ant 173
消息循环是存在于任何本机Windows程序中的一小段代码.它大致如下:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Run Code Online (Sandbox Code Playgroud)
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线程上.消息循环确保此代码运行.
John正在讨论Windows系统(以及其他基于窗口的系统 - X Window,原始Mac OS ....)如何通过消息系统使用事件实现异步用户界面.
在每个应用程序的幕后,都有一个消息传递系统,每个窗口都可以向其他窗口或事件监听器发送事件 - 这是通过向消息队列添加消息来实现的.有一个主循环始终运行查看此消息队列,然后将消息(或事件)分派给侦听器.
维基百科文章Microsoft Windows中的消息循环显示了基本Windows程序的示例代码 - 正如您在最基本的级别所看到的,Windows程序只是"消息泵".
所以,把它们全部拉到一起.设计用于支持UI的Windows程序无法充当服务的原因是因为它需要始终运行消息循环以启用UI支持.如果您将其实现为所描述的服务,它将无法处理内部异步事件处理.
在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并在这里交叉,将导致大量死COM对象甚至整个运行的Word实例泄露.