Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to listen for Windows broadcast messages in .NET?

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?

Bonus Chatter

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);
    }
like image 618
Ian Boyd Avatar asked Feb 17 '12 15:02

Ian Boyd


People also ask

How do you broadcast a message on a server?

Under History, select the message. Under the list, click Edit. The message appears in the Message box. Make your changes, and click Broadcast.


1 Answers

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!

like image 111
Cody Gray Avatar answered Sep 23 '22 13:09

Cody Gray