.NET Windows 服务中的消息泵 [英] Message pump in .NET Windows service

查看:27
本文介绍了.NET Windows 服务中的消息泵的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

除了请求硬件制造商为我们提供不依赖于 Windows 消息泵的 API 之外,还有什么方法可以在我可以通过的 Windows 服务的新线程中手动实例化消息泵进入这个API?我真的必须创建一个完整的 Application 类,还是有一个封装消息泵的较低级别的 .NET 类?

解决方案

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

为了大家的启迪,这是我最终为这个 3rd 方 API 手动启动消息泵所做的工作:

内部类 MessageHandler : NativeWindow{公共事件 EventHandler收到消息;公共消息处理器(){创建句柄(新创建参数());}protected override void WndProc(ref Message msg){//根据您的目的在此处过滤消息事件处理程序<消息数据>处理程序 = MessageReceived;如果(处理程序!= null)处理程序(参考消息);base.WndProc(ref msg);}}公共类 MessagePumpManager{私有只读线程消息泵;private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);公共 StartMessagePump(){//在它自己的线程中启动消息泵messagePump = new Thread(RunMessagePump) {Name = "ManualMessagePump"};messagePump.Start();messagePumpRunning.WaitOne();}//消息泵线程私有无效 RunMessagePump(){//创建控件来处理窗口消息MessageHandler messageHandler = new MessageHandler();//初始化第 3 方 dllDLL.Init(messageHandler.Handle);Console.WriteLine("消息泵线程启动");messagePumpRunning.Set();应用程序运行();}}

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

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

I have a Windows Service written in C# that handles all of our external hardware I/O for a kiosk application. One of our new devices is a USB device that comes with an API in a native DLL. I have a proper P/Invoke wrapper class created. However, this API must be initialized with an HWnd to a windows application because it uses the message pump to raise asynchronous events.

Besides putting in a request to the hardware manufacturer to provide us with an API that does not depend on a Windows message pump, is there any way to manually instantiate a message pump in a new thread in my Windows Service that I can pass into this API? Do I actually have to create a full Application class, or is there a lower level .NET class that encapsulates a message pump?

解决方案

Thanks all for your suggestions. Richard & overslacked, the link you provided in the comments was very helpful. Also, I did not have to allow the service to interact with the desktop in order to manually start a message pump with Application.Run. Apparently, you only need to allow the service to interact with the desktop if you want Windows to start a message pump automatically for you.

For everyone's edification, here is what I ended up doing to manually start a message pump for this 3rd party API:

internal class MessageHandler : NativeWindow
{
    public event EventHandler<MessageData> MessageReceived;

    public MessageHandler ()
    {
        CreateHandle(new CreateParams());
    }

    protected override void WndProc(ref Message msg)
    {
        // filter messages here for your purposes

        EventHandler<MessageData> 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();
    }
}

I had to overcome a few hurdles to get this to work. One is that you need to make certain to create the Form on the same thread that you execute Application.Run. You also can only access the Handle property from that same thread, so I found it easiest to simply initialized the DLL on that thread as well. For all I know, it is expecting to be initialized from a GUI thread anyway.

Also, in my implementation, the MessagePumpManager class is a Singleton instance, so that only one message pump runs for all instances of my device class. Make sure that you truly lazy-initialize your singleton instance if you start the thread in your constructor. If you start the thread from a static context (such as private static MessagePumpManager instance = new MessagePumpManager();) the runtime will never context switch into the newly created thread, and you will deadlock while waiting for the message pump to start.

这篇关于.NET Windows 服务中的消息泵的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆