i have an Object
(i.e. not a form) that wants to listen for broadcast messages from Windows, e.g.:
WM_SETTINGCHANGE
WM_DWMCOLORIZATIONCOLORCHANGED
WM_DWMCOMPOSITIONCHANGED
WM_THEMECHANGED
WM_POWERBROADCAST
What is the mechanism in .NET to setup a non-WinForm's window, that can listen for broadcast messages?
i.e. Is there a WindowsListener
class?
In the olden days, in other development environments, the framework provided an AllocateHwnd
function:
HWND listener = AllocateHWnd(ListenerWindowProc);
where ListenerWindowProc
was my window procedure method:
private void ListenerWindowProc(ref Message msg)
{
switch (msg.msg)
{
case WM_SETTINGCHANGE:
{
...
}
break;
case WM_POWERBROADCAST:
{
...
}
break;
case WM_THEMECHANGED:
{
...
}
break;
...
}
DefWindowProc(msg);
}
The secret sauce was the AllocateHwnd
function:
Pseudo-code:
public static HWND AllocateHWnd(WndMethod method)
{
HWND result;
WNDCLASS tempClass;
Boolean classRegistered;
UtilWindowClass.hInstance := HInstance;
Boolean classRegistered = GetClassInfo(HInstance, UtilWindowClass.lpszClassName, tempClass);
if (!classRegistered || (tempClass.lpfnWndProc != @DefWindowProc)
{
if classRegistered
{
UnregisterClass(utilWindowClass.lpszClassName, HInstance);
RegisterClass(utilWindowClass);
}
result = CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
'', WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, null);
if (!Method != null)
SetWindowLong(result, GWL_WNDPROC, UInt32(MakeObjectInstance(Method)));
}
}
With a whole lot more secret code associated with UtilWindowClass
.
And when you were done you would DeallocateHwnd:
DeallocateHwnd(listenerWindow);
i know somewhere deep in the .NET framework they are going to respond to WM_SETTINGCHANGE
and update internal .NET data structures. i would steal that code, but Reflector finds no reference to WM_SETTINGCHANGE
; presumably because the decompiled code doesn't show the constant name, just the constant 0x001A.
Update:
Note: This object should be self-contained. Anyone using the class should not have to modify their application in order for my class to return correct data. It should listen to broadcasts from Windows itself, and not require a developer to modify their application to listen for certain messages for me (i.e. it should not break encapsulation of the difficult or complication operation)
For example:
public class FrobbingGrobber: IDisposable
{
private IntPtr hwnd = IntPtr.Zero;
public FrobbingGrobber
{
_hwnd = AllocateHwnd(listenerWindowProc);
}
protected virtual void listenerWindowProc(ref Message msg)
{
switch (msg.msg)
{
case WM_DwmColorizationColorChanged, WM_DwmCompositionChanged:
{
UpdateColorizationColor();
}
break;
}
DefWindowProc(msg);
}
public void UpdateColorizationColor()
{
NativeMethods.DwmGetColorizationColorParameters_UndocumentedExport137(ref _color);
}
public void Dispose()
{
Dispose(true);
}
protected void Dispose(Boolean safeToDisposeManagedObjects)
{
if (_hwnd != 0)
{
DeallocateHwnd(_hwnd);
_hwnd = 0;
}
if (safeToDisposeManagedObjects)
GC.SuppressFinalize(this);
}
public ~FrobbingGrobber
{
//If the user forgot to call Dispose i could (correctly) leak a handle,
//or i could fix their mistake for them
Dispose(false);
}
Under History, select the message. Under the list, click Edit. The message appears in the Message box. Make your changes, and click Broadcast.
I assume that the examples you gave were merely that: examples. Because a number of those have managed equivalents that already wrap all of this for you. Like WM_POWERBROADCAST
is wrapped by the Microsoft.Win32.SystemEvents.PowerModeChanged
event. And WM_SETTINGCHANGED
is equivalent to Microsoft.Win32.SystemEvents.UserPreferenceChanged
.
Anyway, broadcasted messages like those you describe are sent to all top-level windows, so all you really need to do is create a top-level window and override its WndProc
method to process the notification messages that you're interested in.
Use the NativeWindow
class to make things easy on yourself. In this case, you don't need everything that is provided by the Form
class, all you need is something to wrap CreateWindowEx
and provide a window procedure. Just create the window without the WS_VISIBLE
flag because you don't want it appearing on the screen.
The .NET Framework does this all over the place internally. For example, the internal TimerNativeWindow
class used for System.Windows.Forms.Timer
. If you want to check out the implementation for yourself in Reflector, start looking there. You should be able to search for constants, but drilling down into a hierarchy of classes for which you know there must be such a notification message handled internally is generally a smarter way to search. The SystemEvents
class (discussed above) is also a good place to start looking for implementation strategies.
Do note that you can't use a message-only window here (HWND_MESSAGE
) because they won't receive broadcast events. The TimerNativeWindow
that I mentioned above does do that, because it doesn't care about broadcast events, so don't just copy and paste the code from there!
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