2010-03-14 18 views
24

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.Bomba de mensajes en .NET Servicio de Windows

Además de solicitar 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?

+4

Revise este hilo, puede ser útil: http://connect.microsoft.com/VisualStudio/feedback/details/241133/detecting-a-wm-timechange-event-in-a-net-windows-service – overslacked

+0

@overslacked, de hecho lo hace. Dice exactamente cómo hacerlo. –

+0

@overslacked Sé que esta es una pregunta antigua, por lo que no sorprende que el enlace de MS Connect ya no funcione. Pero como el contenido de ese enlace parece ser crucial para la discusión aquí, me preguntaba si hay un nuevo enlace disponible. Perdón por estar causando problemas! – Sabuncu

Respuesta

38

Gracias por sus sugerencias. Richard & overslacked, el enlace que proporcionó 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 iniciar manualmente una bomba mensaje para esta API tercera parte:

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

que tuve que superar algunos obstáculos para conseguir 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.

+3

Gracias por tomarse el tiempo para publicar una solución a su problema; me acaba de ahorrar horas de trabajo. – Basic

+0

Gracias ... parece que funciona, sin embargo, en mi caso, como mi 3 ª DLL publica eventos .net y esos eventos se plantean en otro hilo, las cosas no funcionan como se esperaba ... – JobaDiniz

1

Simplemente haga una ventana de solo mensaje, denotada 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); 
+1

¿Pero eso funciona desde un Servicio? –

+0

Ciertamente no es necesario utilizar las llamadas P/Invoke para hacer una ventana. –

+0

No creo que pueda hacer una ventana HWND_MESSAGE sin una llamada a CreateWindowEx. – jaws

2

usted tiene que hacer un formulario, servicios de Windows no interactúan con el escritorio por defecto, así que hay que configurar el servicio para interactuar con el escritorio y su instalación puede ser un poco de dolor. El formulario no será visible sin embargo. Microsoft ha estado haciendo esto cada vez más difícil debido a problemas de seguridad.

+0

Sugerencia interesante. Sin embargo, permitir que el servicio interactúe con el escritorio no es una opción. Ciertamente no quiero que ninguna ventana sea visible. La API en sí no tiene componentes visuales, es solo que decidieron usar una bomba de mensajes para notificar a las personas que llaman sobre eventos asincrónicos. ¿Es un requisito que se permita que el servicio interactúe con el escritorio para ejecutar una bomba de mensajes? – Pickles

+0

La ventana no estaría visible. Sí, es un requisito. –

+0

Consulte https://connect.microsoft.com/VisualStudio/feedback/details/241133/detecting-a-wm-timechange-event-in-a-net-windows-service?wa=wsignin1.0 que tiene paso a paso instrucciones. –

Cuestiones relacionadas