【问题标题】:Message pump in .NET Windows service.NET Windows 服务中的消息泵
【发布时间】:2010-03-14 21:33:37
【问题描述】:

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

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

【问题讨论】:

  • 看看这个帖子,它可能会有所帮助:connect.microsoft.com/VisualStudio/feedback/details/241133/…
  • @overslacked,确实如此。它确切地说明了如何做到这一点。
  • @overslacked 我知道这是一个老问题,因此 MS Connect 链接不再有效也就不足为奇了。但由于该链接的内容似乎对这里的讨论至关重要,我想知道是否有可用的新链接。很抱歉给您添麻烦了!
  • @Sabuncu - 你能发布一个新问题,链接回这个问题,展示你已经尝试过但没有奏效的这个问题的已接受答案的哪些部分?我会密切关注您的问题,看看是否可以提供帮助;否则,我认为接受的答案很好地说明了解决方案。
  • @Sabuncu - 我明白了!在这种情况下,archive.org 似乎有 2014-07-06 的副本 - web.archive.org/web/20140706130218/http://connect.microsoft.com/…

标签: c# .net service message-pump


【解决方案1】:

感谢大家的建议。 Richard & overslacked,您在 cmets 中提供的链接非常有帮助。此外,我不必允许该服务与桌面交互以使用 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 类是一个单例实例,因此我的设备类的所有实例只运行一个消息泵。如果您在构造函数中启动线程,请确保您真正延迟初始化单例实例。如果从静态上下文(例如私有静态 MessagePumpManager instance = new MessagePumpManager();)启动线程,则运行时将永远不会上下文切换到新创建的线程,并且在等待消息泵启动时会死锁。

【讨论】:

  • 感谢您抽出宝贵时间为您的问题发布解决方案 - 您为我节省了数小时的工作时间。
  • 谢谢...似乎可以工作,但是在我的情况下,因为我的第三个 DLL 发布 .net 事件并且这些事件在另一个线程中引发,所以事情没有按预期工作...跨度>
  • Issue-1:StartMessagePump 不是构造函数,因此无法初始化只读成员。
  • @F.I.V 我大约 10 年前使用 .NET 2.0(也许是 3.5?)编写了这段代码。我怀疑某些东西必须针对当前版本进行修改。创建一个构造函数来初始化 Thread 对象,或者如果它是一个问题,则删除只读。
【解决方案2】:

你必须制作一个Form,Windows服务默认不与桌面交互,所以你必须设置服务与桌面交互,安装它可能有点麻烦。该表格将不可见。由于安全问题,Microsoft 一直在故意让这件事变得越来越难。

【讨论】:

  • 有趣的建议。但是,不允许该服务与桌面交互。我当然不希望任何窗口可见。 API 本身没有可视化组件,只是他们决定使用消息泵来通知调用者异步事件。是否需要允许服务与桌面交互才能运行消息泵?
  • 窗口将不可见。是的,这是一项要求。
【解决方案3】:

只需创建一个仅显示消息的窗口,在 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);

【讨论】:

  • 当然不需要使用 P/Invoke 调用来创建窗口。
  • 我认为如果不调用 CreateWindowEx 就无法创建 HWND_MESSAGE 窗口。
  • 只要消息泵将从未配置为与桌面交互的 Windows 服务运行,我在这条路线上没有问题。我明天试一试。
  • 在服务中创建 Winform 对我来说听起来很疯狂。仅消息窗口与标准窗口不同。一个只有消息的窗口只有一个消息泵——这就是这个人正在寻找的全部。除此之外的任何东西,比如一个 Windows 窗体类都是多余的。
  • 实际上有一个名为NativeWindow (msdn.microsoft.com/en-us/library/…) 的类,它允许在调用CreateWindowEx 时使用自定义参数WndProc
猜你喜欢
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-05
相关资源
最近更新 更多