Подтвердить что ты не робот

Сообщение насоса в службе .NET Windows

У меня есть служба Windows, написанная на С#, которая обрабатывает весь наш внешний аппаратный ввод-вывод для приложения киоска. Одним из наших новых устройств является USB-устройство, которое поставляется с API в родной DLL. У меня есть собственный класс оболочки P/Invoke. Однако этот API должен быть инициализирован HWnd для приложения Windows, потому что он использует насос сообщений для повышения асинхронных событий.

Помимо запроса производителю оборудования предоставить нам API, который не зависит от насоса сообщений Windows, есть ли способ вручную создать поток сообщений в новом потоке моей Службы Windows, который я могу пройти в этот API? Должен ли я на самом деле создавать полный класс приложения или есть класс .NET более низкого уровня, который инкапсулирует насос сообщений?

4b9b3361

Ответ 1

Спасибо всем за ваши предложения. Ричард и переполненный, ссылка, которую вы предоставили в комментариях, была очень полезна. Кроме того, мне не нужно было позволять службе взаимодействовать с рабочим столом, чтобы вручную запустить насос сообщений с помощью Application.Run. По-видимому, вам нужно только разрешить службе взаимодействовать с рабочим столом, если вы хотите, чтобы Windows автоматически запускала насос сообщений для вас.

Для каждого назидания, вот что я в итоге сделал, чтобы вручную запустить насос сообщений для этого стороннего 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 в этом потоке. Насколько я знаю, он все равно будет инициализирован из потока графического интерфейса.

Кроме того, в моей реализации класс MessagePumpManager является экземпляром Singleton, так что для всех экземпляров моего класса устройств запускается только один насос сообщений. Убедитесь, что вы действительно ленивы инициализируете свой экземпляр singleton, если вы начинаете поток в своем конструкторе. Если вы запускаете поток из статического контекста (например, private static MessagePumpManager instance = new MessagePumpManager();), среда выполнения никогда не переключит контекст во вновь созданный поток, и вы будете заторможены в ожидании запуска насоса сообщений.

Ответ 2

Вы должны создать форму, службы Windows не взаимодействуют с рабочим столом по умолчанию, поэтому вам нужно настроить службу для взаимодействия с рабочим столом и ее установка может быть немного больной. Форма не будет видна, хотя. Microsoft намеренно делает это сложнее и сложнее из-за проблем с безопасностью.

Ответ 3

Просто создайте окно только для сообщений, обозначенное параметром HWND_MESSAGE при вызове CreateWindowEx. Конечно, это код 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);