我有一个Object
(即不是表格)想要收听来自Windows的广播消息,例如:
WM_SETTINGCHANGE
WM_DWMCOLORIZATIONCOLORCHANGED
WM_DWMCOMPOSITIONCHANGED
WM_THEMECHANGED
WM_POWERBROADCAST
.NET中设置非WinForm窗口的机制是什么,可以监听广播消息?
即。有WindowsListener
班吗?
在过去,在其他开发环境中,框架提供了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);
}
答案 0 :(得分:7)
我认为你给出的例子仅仅是:例子。因为其中许多已经管理了等价物,已经为您包装了所有这些。就像WM_POWERBROADCAST
包裹WM_SETTINGCHANGED
一样。 WndProc
相当于Microsoft.Win32.SystemEvents.PowerModeChanged
event。
无论如何,您描述的广播消息会被发送到所有顶级窗口,所以您真正需要做的就是创建一个顶级窗口并覆盖其Form
方法来处理您的通知消息对此感兴趣。
使用Microsoft.Win32.SystemEvents.UserPreferenceChanged
让自己轻松自如。在这种情况下,您不需要CreateWindowEx
类提供的所有内容,只需要包装WS_VISIBLE
并提供窗口过程即可。只需创建没有TimerNativeWindow
标志的窗口,因为您不希望它出现在屏幕上。
.NET Framework在内部遍布整个地方。例如,用于System.Windows.Forms.Timer
的内部SystemEvents
类。如果您想在Reflector中查看自己的实现,请从那里开始查看。您应该能够搜索常量,但深入了解类的层次结构,您知道必须在内部处理这样的通知消息,这通常是一种更智能的搜索方式。 HWND_MESSAGE
类(如上所述)也是开始寻找实施策略的好地方。
请注意,您不能在此处使用仅限消息的窗口(TimerNativeWindow
),因为NativeWindow
class。我上面提到的{{1}} 这样做,因为它不关心广播事件,所以不要只是从那里复制并粘贴代码!