Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Message pump in .NET Windows service

I have a Windows Service written in C# that handles all of our external hardware I/O for a kiosk application. One of our new devices is a USB device that comes with an API in a native DLL. I have a proper P/Invoke wrapper class created. However, this API must be initialized with an HWnd to a windows application because it uses the message pump to raise asynchronous events.

Besides putting in a request to the hardware manufacturer to provide us with an API that does not depend on a Windows message pump, is there any way to manually instantiate a message pump in a new thread in my Windows Service that I can pass into this API? Do I actually have to create a full Application class, or is there a lower level .NET class that encapsulates a message pump?

like image 751
Pickles Avatar asked Mar 14 '10 21:03

Pickles


People also ask

What is Windows message pump?

The "message pump" is a core part of any Windows program that is responsible for dispatching windowing messages to the various parts of the application. This is the core of Win32 UI programming.

What is a message loop C#?

The message loop is an obligatory section of code in every program that uses a graphical user interface under Microsoft Windows. Windows programs that have a GUI are event-driven. Windows maintains an individual message queue for each thread that has created a window. Usually only the first thread creates windows.

How would you describe message pump?

Message pumps are said to 'pump' messages from the program's message queue (assigned and usually owned by the underlying operating system) into the program for processing. In the strictest sense, an event loop is one of the methods for implementing inter-process communication.


3 Answers

Thanks all for your suggestions. Richard & overslacked, the link you provided in the comments was very helpful. Also, I did not have to allow the service to interact with the desktop in order to manually start a message pump with Application.Run. Apparently, you only need to allow the service to interact with the desktop if you want Windows to start a message pump automatically for you.

For everyone's edification, here is what I ended up doing to manually start a message pump for this 3rd party 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();     } } 

I had to overcome a few hurdles to get this to work. One is that you need to make certain to create the Form on the same thread that you execute Application.Run. You also can only access the Handle property from that same thread, so I found it easiest to simply initialized the DLL on that thread as well. For all I know, it is expecting to be initialized from a GUI thread anyway.

Also, in my implementation, the MessagePumpManager class is a Singleton instance, so that only one message pump runs for all instances of my device class. Make sure that you truly lazy-initialize your singleton instance if you start the thread in your constructor. If you start the thread from a static context (such as private static MessagePumpManager instance = new MessagePumpManager();) the runtime will never context switch into the newly created thread, and you will deadlock while waiting for the message pump to start.

like image 93
Pickles Avatar answered Sep 21 '22 22:09

Pickles


You have to make a Form, Windows services do not interact with the desktop by default, so you have to set the service to interact with the desktop and installing it can be a bit of a pain. The Form will not be visible though. Microsoft has been deliberately making this harder and harder to do because of security issues.

like image 28
Richard Anthony Freeman-Hein Avatar answered Sep 19 '22 22:09

Richard Anthony Freeman-Hein


Just make a message-only window, denoted by the HWND_MESSAGE parameter in the call to CreateWindowEx. Granted, this is C code, but you can easily make these structs and P/Invoke calls in 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);
like image 24
jaws Avatar answered Sep 22 '22 22:09

jaws