27

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

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

4

3 回答 3

42

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

为了大家的启迪,这是我最终为这个第 3 方 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();
    }
}

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

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

于 2010-03-16T19:33:54.553 回答
2

您必须制作一个Form,Windows服务默认不与桌面交互,因此您必须将服务设置为与桌面交互并且安装它可能有点痛苦。该表格将不可见。由于安全问题,微软一直故意让这件事变得越来越难。

于 2010-03-14T22:14:45.330 回答
1

只需创建一个仅显示消息的窗口,在 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);
于 2010-03-14T21:45:27.260 回答