.NET Windows服务中的消息泵

我有一个用C#编写的Windows服务,它处理自助服务终端应用程序的所有外部硬件I / O. 我们的新设备之一是USB设备,它在本机DLL中附带API。 我创建了一个正确的P / Invoke包装类。 但是,必须使用HWnd将此API初始化为Windows应用程序,因为它使用消息泵来引发异步事件。

除了向硬件制造商提出请求以向我们提供不依赖于Windows消息泵的API之外,有没有办法在我的Windows服务中的新线程中手动实例化消息泵,我可以将其传递给此API ? 我是否真的必须创建一个完整的Application类,或者是否有一个封装消息泵的低级.NET类?

谢谢大家的建议。 Richard&overlacked,您在评论中提供的链接非常有帮助。 此外,我不必允许服务与桌面交互,以便使用Application.Run手动启动消息泵。 显然,如果您希望Windows自动为您启动消息泵,您只需要允许服务与桌面交互。

对于每个人的启发,以下是我最终为第三方API手动启动消息泵所做的事情:

internal class MessageHandler : NativeWindow { public event EventHandler MessageReceived; public MessageHandler () { CreateHandle(new CreateParams()); } protected override void WndProc(ref Message msg) { // filter messages here for your purposes EventHandler handler = MessageReceived; if (handler != null) handler(ref msg); base.WndProc(ref msg); } } public class MessagePumpManager { private readonly Thread messagePump; private AutoResetEvent messagePumpRunning = new AutoResetEvent(false); public StartMessagePump() { // start message pump in its own thread messagePump = new Thread(RunMessagePump) {Name = "ManualMessagePump"}; messagePump.Start(); messagePumpRunning.WaitOne(); } // Message Pump Thread private void RunMessagePump() { // Create control to handle windows messages MessageHandler messageHandler = new MessageHandler(); // Initialize 3rd party dll DLL.Init(messageHandler.Handle); Console.WriteLine("Message Pump Thread Started"); messagePumpRunning.Set(); Application.Run(); } } 

我必须克服一些障碍才能让它发挥作用。 一个是你需要确保在执行Application.Run的同一个线程上创建Form。 您也只能从同一个线程访问Handle属性,因此我发现在该线程上简单地初始化DLL也是最简单的。 据我所知,无论如何,它都希望从GUI线程初始化。

此外,在我的实现中,MessagePumpManager类是一个Singleton实例,因此只有一个消息泵运行我的设备类的所有实例。 如果在构造函数中启动线程,请确保您真正延迟初始化单例实例。 如果从静态上下文(例如私有静态MessagePumpManager instance = new MessagePumpManager();)启动线程,则运行时永远不会将上下文切换到新创建的线程,并且在等待消息泵启动时您将死锁。

您必须创建一个表单,Windows服务默认情况下不与桌面交互,因此您必须将服务设置为与桌面交互并安装它可能会有点痛苦。 但是表格不可见。 由于安全问题,微软一直在故意这么做。

只需创建一个仅消息窗口,在CreateWindowEx调用中由HWND_MESSAGE参数表示。 当然,这是C代码,但您可以轻松地在C#中进行这些结构和P / Invoke调用。

 WNDCLASS w; HWND handle; w.hInstance = (HINSTANCE)GetModuleHandle(...); // Associate this module with the window. w.lpfnWndProc = ... // Your windowproc w.lpszClassName = ... // Name of your window class RegisterClass(&w) handle = CreateWindowEx(0, w.lpszClassName, w.lpszClassName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wc.hInstance, NULL);