在 WinForm 中一般采用重写 WndProc 的方法对窗口或控件接受到的指定消息进行处理
示例:禁止通过关闭按钮或其他发送 WM_CLOSE 消息的途径关闭窗口
| 
 | protected override void WndProc(ref Message m) | 
| 
 | { | 
| 
 | const int WM_CLOSE = 0x0010; | 
| 
 | if(m.Msg == WM_CLOSE) | 
| 
 | { | 
| 
 |  | 
| 
 | return; | 
| 
 | } | 
| 
 | base.WndProc(ref m); | 
| 
 | } | 
Control 类中还有个 DefWndProc 为默认的窗口过程
WPF HwndSource
WPF 仅本机窗口或 HwndHost 嵌入控件拥有句柄,可通过 HwndSource 添加消息处理
示例:禁止通过关闭按钮或其他发送 WM_CLOSE 消息的途径关闭窗口
| 
 | HwndSource source = null; | 
| 
 | 
 | 
| 
 | protected override void OnSourceInitialized(EventArgs e) | 
| 
 | { | 
| 
 | base.OnSourceInitialized(e); | 
| 
 | IntPtr handle = new WindowInteropHelper(this).Handle; | 
| 
 | source = HwndSource.FromHandle(handle); | 
| 
 | source.AddHook(WndProc); | 
| 
 | } | 
| 
 | 
 | 
| 
 | protected override void OnClosed(EventArgs e) | 
| 
 | { | 
| 
 | source?.RemoveHook(WndProc); | 
| 
 | base.OnClosed(e); | 
| 
 | } | 
| 
 | 
 | 
| 
 | private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) | 
| 
 | { | 
| 
 | const int WM_CLOSE = 0x0010; | 
| 
 | if(msg == WM_CLOSE) | 
| 
 | { | 
| 
 |  | 
| 
 | handled = true; | 
| 
 | } | 
| 
 | return IntPtr.Zero; | 
| 
 | } | 
⚠ 注意:1.消息过滤器对于特定线程是唯一的;2.使用消息过滤器可能会降低程序性能
IMessageFilter 接口允许程序在将消息调度到控件或窗口之前捕获消息进行预处理
IMessageFilter 的 PreFilterMessage 与 Control 的 WndProc 接收到的消息是一个交集关系,应用程序接收到的消息来自系统消息队列,相对来说更全,但会有部分消息会直接发送到窗口或控件而不进入系统消息队列
实现 IMessageFilter 接口实例可对整个线程消息循环进行预处理,并根据 m.HWnd 获取消息传入的窗口或控件句柄
示例:截获程序鼠标悬浮消息,窗口标题显示当前悬浮控件名
| 
 | static class Program | 
| 
 | { | 
| 
 | [STAThread] | 
| 
 | static void Main() | 
| 
 | { | 
| 
 | Application.EnableVisualStyles(); | 
| 
 | Application.SetCompatibleTextRenderingDefault(false); | 
| 
 | var filter = new SampleMsgFilter(); | 
| 
 | Application.AddMessageFilter(filter); | 
| 
 | Application.Run(new MainForm()); | 
| 
 | Application.RemoveMessageFilter(filter); | 
| 
 | } | 
| 
 | } | 
| 
 | 
 | 
| 
 | sealed class SampleMsgFilter : IMessageFilter | 
| 
 | { | 
| 
 | public bool PreFilterMessage(ref Message m) | 
| 
 | { | 
| 
 | const int WM_MOUSEHOVER = 0x02A1; | 
| 
 | if(m.Msg == WM_MOUSEHOVER && Control.FromHandle(m.HWnd) is Control ctr) | 
| 
 | { | 
| 
 | ctr.FindForm().Text = ctr.Name; | 
| 
 | return true; | 
| 
 | } | 
| 
 | return false; | 
| 
 | } | 
| 
 | } | 
NativeWindow 是 IWin32Window 的低级封装,并且和 WinForm Control 一样拥有 WndProc 和 DefWndProc 方法,故同样可通过重写 WndProc 方法处理消息
可以通过 CreateHandle(new CreateParams()) 创建没有 UI 的仅消息循环的窗口。比如托盘图标类 NotifyIcon 内部会创建一个 NativeWindow 用来接收任务栏创建消息 WM_TASKBARCREATED ("TaskbarCreated"),在资源管理器崩溃重启后重新创建图标。
附加到其他窗口
由于 WinForm Control WndProc 是密封的,处理消息时必须继承类型并重写,需要单独进行消息处理的窗口或控件较多时,对原代码具有很大的侵入性;而 IMessageFilter 是针对整个应用程序的消息循环,官方文档说使用消息过滤器很可能会降低程序性能;相对来说,由于 HwndSource AddHook 和 RemoveHook 不是密封的,WPF 程序可以在不侵入原代码的条件下处理窗口消息,在可复用性上面反而还具有优势。但如果仔细看看 NativeWindow 源代码,会发现它内部调用了 SetWindowLong GWL_WNDPROC (窗口子类化),可以通过 AssignHandle 附加到任意窗口或控件进行消息处理,这个窗口不限制类型,甚至可以附加到其他程序窗口。
这里提供一个静态辅助类,借助 NativeWindow 简化附加窗口消息过程处理操作
| 
 | using System; | 
| 
 | using System.Collections.Generic; | 
| 
 | using System.Windows.Forms; | 
| 
 | 
 | 
| 
 | namespace Wondershare.WinTool.Helpers | 
| 
 | { | 
| 
 | public delegate bool HookProc(ref Message m); | 
| 
 | 
 | 
| 
 | public static class MessageHooker | 
| 
 | { | 
| 
 | sealed class HookWindow : NativeWindow | 
| 
 | { | 
| 
 | List<KeyValuePair<HookProc, Action>> hooks; | 
| 
 | 
 | 
| 
 | public HookWindow(IntPtr hWnd) | 
| 
 | { | 
| 
 | AssignHandle(hWnd); | 
| 
 | } | 
| 
 | 
 | 
| 
 | public void AddHookProc(HookProc hook, Action removedHandler) | 
| 
 | { | 
| 
 | if (hooks == null) | 
| 
 | { | 
| 
 | hooks = new List<KeyValuePair<HookProc, Action>>(); | 
| 
 | } | 
| 
 | hooks.Insert(0, new KeyValuePair<HookProc, Action>(hook, removedHandler)); | 
| 
 | } | 
| 
 | 
 | 
| 
 | public void RemoveHookProc(HookProc hook) | 
| 
 | { | 
| 
 | if (hooks != null) | 
| 
 | { | 
| 
 | for (int i = hooks.Count - 1; i >= 0; i--) | 
| 
 | { | 
| 
 | if (hooks[i].Key == hook) | 
| 
 | { | 
| 
 | hooks[i].Value?.Invoke(); | 
| 
 | hooks.RemoveAt(i); | 
| 
 | } | 
| 
 | } | 
| 
 | } | 
| 
 | } | 
| 
 | 
 | 
| 
 | protected override void WndProc(ref Message m) | 
| 
 | { | 
| 
 | if (hooks != null) | 
| 
 | { | 
| 
 | foreach (var hook in hooks) | 
| 
 | { | 
| 
 | if (hook.Key(ref m)) return; | 
| 
 | } | 
| 
 | const int WM_NCDESTORY = 0x0082; | 
| 
 | if (m.Msg == WM_NCDESTROY) | 
| 
 | { | 
| 
 | for (int i = hooks.Count - 1; i >= 0; i--) | 
| 
 | { | 
| 
 | hooks[i].Value?.Invoke(); | 
| 
 | } | 
| 
 | hooks = null; | 
| 
 | } | 
| 
 | base.WndProc(ref m); | 
| 
 | } | 
| 
 | } | 
| 
 | } | 
| 
 | 
 | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 | public static void AddHook(IntPtr handle, HookProc hook, Action removedHandler = null) | 
| 
 | { | 
| 
 | if (!(NativeWindow.FromHandle(handle) is HookWindow window)) | 
| 
 | { | 
| 
 | window = new HookWindow(handle); | 
| 
 | } | 
| 
 | window.AddHookProc(hook, removedHandler); | 
| 
 | } | 
| 
 | 
 | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 | public static void RemoveHook(IntPtr handle, HookProc hook) | 
| 
 | { | 
| 
 | if (NativeWindow.FromHandle(handle) is HookWindow window) | 
| 
 | { | 
| 
 | window.RemoveHookProc(hook); | 
| 
 | } | 
| 
 | } | 
| 
 | } | 
| 
 | } | 
转自https://www.cnblogs.com/BluePointLilac/p/18802906