Bomba de mensajes en el servicio .NET Windows

Tengo un servicio de Windows escrito en C # que maneja todas nuestras E / S de hardware externo para una aplicación de kiosco. Uno de nuestros nuevos dispositivos es un dispositivo USB que viene con una API en una DLL nativa. Tengo una clase de contenedor P / Invoke adecuada creada. Sin embargo, esta API se debe inicializar con un HWnd a una aplicación de Windows porque usa la bomba de mensajes para generar eventos asincrónicos.

Además de pedirle al fabricante del hardware que nos proporcione una API que no dependa de una bomba de mensajes de Windows, ¿hay alguna forma de instanciar manualmente una bomba de mensajes en un nuevo hilo en mi Servicio de Windows que pueda pasar a esta API? ? ¿Tengo que crear una clase de aplicación completa o hay una clase .NET de nivel inferior que encapsula una bomba de mensajes?

Gracias a todos por sus sugerencias. Richard y overslacked, el enlace que brindó en los comentarios fue muy útil. Además, no tuve que permitir que el servicio interactuara con el escritorio para iniciar manualmente una bomba de mensajes con Application.Run. Aparentemente, solo necesita permitir que el servicio interactúe con el escritorio si desea que Windows inicie automáticamente un mensaje para usted.

Para la edificación de todos, esto es lo que terminé haciendo para iniciar manualmente una bomba de mensajes para esta API de terceros:

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

Tuve que superar algunos obstáculos para que esto funcione. Una es que debe asegurarse de crear el Formulario en el mismo hilo que ejecuta Application.Run. También solo puede acceder a la propiedad Handle desde ese mismo hilo, así que me resultó más fácil simplemente inicializar el DLL en ese hilo también. Por lo que sé, está esperando ser inicializado desde un hilo GUI de todos modos.

Además, en mi implementación, la clase MessagePumpManager es una instancia de Singleton, de modo que solo se ejecuta una bomba de mensajes para todas las instancias de la clase de mi dispositivo. Asegúrate de inicializar realmente tu instancia singleton si comienzas el hilo en tu constructor. Si inicia el hilo desde un contexto estático (como la instancia de MessagePumpManager estática privada = new MessagePumpManager ();) el tiempo de ejecución nunca cambiará de contexto al hilo recién creado, y se bloqueará mientras espera que se inicie el bombeo de mensajes.

Debes crear un Formulario, los servicios de Windows no interactúan con el escritorio de manera predeterminada, por lo que debes configurar el servicio para que interactúe con el escritorio y su instalación puede ser un poco dolorosa. El formulario no será visible sin embargo. Microsoft ha estado haciendo esto cada vez más difícil debido a problemas de seguridad.

Simplemente haga una ventana de solo mensaje, indicada por el parámetro HWND_MESSAGE en la llamada a CreateWindowEx. Por supuesto, este es el código C, pero puede hacer fácilmente estas estructuras y llamadas P / Invoke en C #.

 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);