如何在.NET中监听Windows广播消息?
我有一个对象
(即不是表单)想要监听来自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.:
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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为您给出的例子仅仅是:例子。因为其中许多管理工具已经为您封装了所有这些内容。就像
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 theMicrosoft.Win32.SystemEvents.PowerModeChanged
event. AndWM_SETTINGCHANGED
is equivalent toMicrosoft.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 theForm
class, all you need is something to wrapCreateWindowEx
and provide a window procedure. Just create the window without theWS_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 forSystem.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. TheSystemEvents
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. TheTimerNativeWindow
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!