如何在.NET中监听Windows广播消息?

发布于 2025-01-06 19:37:31 字数 4355 浏览 3 评论 0原文

我有一个对象(即不是表单)想要监听来自Windows的广播消息,例如:

  • WM_SETTINGCHANGE
  • WM_DWMCOLORIZATIONCOLORCHANGED< /一>
  • <一href="http://msdn.microsoft.com/en-us/library/windows/desktop/dd388199%28v=vs.85%29.aspx" rel="nofollow">WM_DWMCOMPOSITIONCHANGED< /一>
  • <一href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms632650%28v=vs.85%29.aspx" rel="nofollow">WM_THEMECHANGED< /一>
  • <一href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa373247%28v=vs.85%29.aspx" rel="nofollow">WM_POWERBROADCAST< /a>

.NET 中设置非 WinForm 窗口以侦听广播消息的机制是什么?

即是否有 WindowsListener 类?

Bonus Chatter

在过去,在其他开发环境中,框架提供了 AllocateHwnd 函数:

HWND listener = AllocateHWnd(ListenerWindowProc);

其中 ListenerWindowProc 是我的窗口过程方法:

private void ListenerWindowProc(ref Message msg)
{
    switch (msg.msg)
    {
       case WM_SETTINGCHANGE: 
       {
          ...
       }
       break;
       case WM_POWERBROADCAST:
       {
          ...
       }
       break;
       case WM_THEMECHANGED: 
       {
          ...
       }
       break;
       ...
   }
   DefWindowProc(msg);
}

秘密武器是 AllocateHwnd 函数:

伪代码:

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)));
   }
}

具有与 UtilWindowClass 关联的更多秘密代码。

完成后,您将 DeallocateHwnd

DeallocateHwnd(listenerWindow);

我知道他们将在 .NET 框架深处的某个地方做出响应WM_SETTINGCHANGE 并更新内部 .NET 数据结构。我会窃取该代码,但 Reflector 找不到对 WM_SETTINGCHANGE 的引用;大概是因为反编译的代码不显示常量名称,只显示常量0x001A

更新

注意:该对象应该是独立的。使用该类的任何人都不必为了让我的类返回正确的数据而修改他们的应用程序。它应该监听来自Windows本身的广播,并且不需要开发人员修改他们的应用程序来为我监听某些消息(即它不应该破坏困难或复杂操作的封装)

例如:

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);
    }

i have an Object (i.e. not a form) that wants to listen for broadcast messages from Windows, e.g.:

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);
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心房的律动 2025-01-13 19:37:31

我认为您给出的例子仅仅是:例子。因为其中许多管理工具已经为您封装了所有这些内容。就像 WM_POWERBROADCAST包装一样Microsoft.Win32.SystemEvents.PowerModeChanged 事件。并且 WM_SETTINGCHANGED 相当于 <代码>Microsoft.Win32.SystemEvents.UserPreferenceChanged

无论如何,像您所描述的那样的广播消息会发送到所有顶级窗口,因此您真正需要做的就是创建一个顶级窗口并重写其 WndProc 方法来处理您发送的通知消息感兴趣。

使用 NativeWindow class让事情变得简单。在这种情况下,您不需要 Form 类提供的所有内容,您需要的只是包装 CreateWindowEx 并提供窗口过程。只需创建不带 WS_VISIBLE 标志的窗口,因为您不希望它出现在屏幕上。

.NET Framework 在内部各处都执行此操作。例如,用于System.Windows.Forms.Timer 的内部TimerNativeWindow 类。如果您想亲自检查 Reflector 中的实现,请开始在那里查找。您应该能够搜索常量,但是深入了解您知道必须有内部处理的此类通知消息的类的层次结构通常是更智能的搜索方式。 SystemEvents 类(如上所述)也是开始寻找实现策略的好地方。

请注意,您不能在此处使用仅消息窗口 (HWND_MESSAGE),因为 他们不会收到广播事件。我上面提到的 TimerNativeWindow 确实做到了这一点,因为它不关心广播事件,所以不要只是从那里复制并粘贴代码!

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文