WndProc is one of the darker parts of the dreaded Windows API that everyone prefers to avoid, until they realize there are just some things that cannot be implemented without it. WndProc is typically used for messages that notify of a particular system event, such as monitor going into power saving, USB device inserted, global hotkey pressed, display settings changed, session ending and many many more. In order to be able to receive and process these messages, you really only need a valid window and to register for the particular messages you are interested in. Accessing the WndProc flow in WinForms is as easy as overriding the corresponding method in one of the Forms, but WPF Windows do not directly expose it.
Contrary to what many people seem to think (according to terrible StackOverflow answers), being able to process window messages in WPF is both required and is definitely possible. WPF actually lets you listen to messages in a variety of ways:
- Using HwndSource hooking
This will use the application’s main window as the host, as it’s typically the window that stays open for as long as the application is running. You can specify a different windows as a parameter to
FromVisual(…) method, but make sure to call
source.Dispose() after you’re done.
- Using ComponentDispatcher events
This time we use
WindowInteropHelper just to get the window handle (hWnd), but the actual message processing is done by subscribing to
ComponentDispatcher static class. As the event is static, you will probably want to unsubscribe from it later.
Now the problem is that both of these approaches are not MVVM compliant and here’s why. Typically you will want to do all your Windows API stuff in a specific encapsulated service, but since both these approaches require an existing window, those services have to be initialized separately from the others, typically after Loaded event fires on that window. You can’t really initialize the window before you create services and view models, because the window itself is dependent on those services and viewmodels to be initialized. Even if you disregard all the circular dependencies, best case scenario is that your services will be tightly coupled with your views, which is unwanted.
In order to completely isolate yourself from all these problems, you need to create the specialized “sponge” window in the services that require it. Conveniently
System.Windows.Forms.NativeWindow fits exactly this purpose. Remember to reference
System.Windows.Forms in your project, while you’re cringing at the thought (I know I was).
So to summarize this code – there is a reusable singleton
SpongeWindow, a special kind of window that doesn’t do anything, but receives messages and routes them to each class that inherits
WinApiServiceBase. A handle to the Sponge window is available to inheriting classes, so that they can pass it when registering for specific kind of messages.
Don’t forget to call
base.WndProc(ref m) inside the overriden
SpongeWindow.WndProc(…), otherwise it won’t be able to create a handle for itself!
Finally, here is how you make use of this: