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?
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.
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.
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.
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.
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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With