C# 窗口过程消息处置惩罚 WndProc

打印 上一主题 下一主题

主题 1961|帖子 1961|积分 5883

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
C# 窗口过程消息处置惩罚 WndProc

WinForm WndProc

在 WinForm 中一般采用重写 WndProc 的方法对窗口或控件接受到的指定消息进行处置惩罚
示例:克制通过关闭按钮或其他发送 WM_CLOSE 消息的途径关闭窗口
  1. protected override void WndProc(ref Message m)
  2. {
  3.     const int WM_CLOSE = 0x0010;
  4.     if(m.Msg == WM_CLOSE)
  5.     {
  6.         // MessageBox.Show("禁止关闭此窗口");
  7.         return;
  8.     }
  9.     base.WndProc(ref m);
  10. }
复制代码
Control 类中还有个 DefWndProc 为默认的窗口过程
WPF HwndSource

WPF 仅本机窗口或 HwndHost 嵌入控件拥有句柄,可通过 HwndSource 添加消息处置惩罚
示例:克制通过关闭按钮或其他发送 WM_CLOSE 消息的途径关闭窗口
  1. HwndSource source = null;
  2. protected override void OnSourceInitialized(EventArgs e)
  3. {
  4.     base.OnSourceInitialized(e);
  5.     IntPtr handle = new WindowInteropHelper(this).Handle;
  6.     source = HwndSource.FromHandle(handle);
  7.     source.AddHook(WndProc);
  8. }
  9. protected override void OnClosed(EventArgs e)
  10. {
  11.     source?.RemoveHook(WndProc);
  12.     base.OnClosed(e);
  13. }
  14. private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
  15. {
  16.     const int WM_CLOSE = 0x0010;
  17.     if(msg == WM_CLOSE)
  18.     {
  19.         // MessageBox.Show("禁止关闭此窗口");
  20.         handled = true; // 标记为已处理
  21.     }
  22.     return IntPtr.Zero;
  23. }
复制代码
WinForm IMessageFilter

⚠ 注意:1.消息过滤器对于特定线程是唯一的;2.使用消息过滤器可能会降低程序性能
IMessageFilter 接口允许程序在将消息调度到控件或窗口之前捕获消息进行预处置惩罚
IMessageFilter 的 PreFilterMessage 与 Control 的 WndProc 接收到的消息是一个交集关系,应用程序接收到的消息来自体系消息队列,相对来说更全,但会有部分消息会直接发送到窗口或控件而不进入体系消息队列
实现 IMessageFilter 接口实例可对整个线程消息循环进行预处置惩罚,并根据 m.HWnd 获取消息传入的窗口或控件句柄
示例:截获程序鼠标悬浮消息,窗口标题显示当前悬浮控件名
  1. static class Program
  2. {
  3.     [STAThread]
  4.     static void Main()
  5.     {
  6.         Application.EnableVisualStyles();
  7.         Application.SetCompatibleTextRenderingDefault(false);
  8.         var filter = new SampleMsgFilter();
  9.         Application.AddMessageFilter(filter); // 添加到消息泵
  10.         Application.Run(new MainForm());
  11.         Application.RemoveMessageFilter(filter); // 从消息泵移除
  12.     }
  13. }
  14. sealed class SampleMsgFilter : IMessageFilter
  15. {
  16.     public bool PreFilterMessage(ref Message m)
  17.     {
  18.         const int WM_MOUSEHOVER = 0x02A1;
  19.         if(m.Msg == WM_MOUSEHOVER && Control.FromHandle(m.HWnd) is Control ctr)
  20.         {
  21.             ctr.FindForm().Text = ctr.Name;
  22.             return true; // 过滤消息不继续派发
  23.         }
  24.         return false; // 允许消息派发到下一个过滤器或控件
  25.     }
  26. }
复制代码
WinForm NativeWindow

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 简化附加窗口消息过程处置惩罚利用:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Windows.Forms;
  4. namespace Wondershare.WinTool.Helpers
  5. {
  6.   public delegate bool HookProc(ref Message m);
  7.     public static class MessageHooker
  8.     {
  9.         sealed class HookWindow : NativeWindow
  10.         {
  11.             List<KeyValuePair<HookProc, Action>> hooks;
  12.             public HookWindow(IntPtr hWnd)
  13.             {
  14.                 AssignHandle(hWnd);
  15.             }
  16.             public void AddHookProc(HookProc hook, Action removedHandler)
  17.             {
  18.                 if (hooks == null)
  19.                 {
  20.                     hooks = new List<KeyValuePair<HookProc, Action>>();
  21.                 }
  22.                 hooks.Insert(0, new KeyValuePair<HookProc, Action>(hook, removedHandler));
  23.             }
  24.             public void RemoveHookProc(HookProc hook)
  25.             {
  26.                 if (hooks != null)
  27.                 {
  28.                     for (int i = hooks.Count - 1; i >= 0; i--)
  29.                     {
  30.                         if (hooks[i].Key == hook)
  31.                         {
  32.                             hooks[i].Value?.Invoke();
  33.                             hooks.RemoveAt(i);
  34.                         }
  35.                     }
  36.                 }
  37.             }
  38.             protected override void WndProc(ref Message m)
  39.             {
  40.                 if (hooks != null)
  41.                 {
  42.                     foreach (var hook in hooks)
  43.                     {
  44.                         if (hook.Key(ref m)) return;
  45.                     }
  46.                     const int WM_NCDESTORY = 0x0082;
  47.                     if (m.Msg == WM_NCDESTROY) // 窗口销毁时移除所有 hook
  48.                     {
  49.                         for (int i = hooks.Count - 1; i >= 0; i--)
  50.                         {
  51.                             hooks[i].Value?.Invoke();
  52.                         }
  53.                         hooks = null;
  54.                     }
  55.                     base.WndProc(ref m);
  56.                 }
  57.             }
  58.         }
  59.         /// <summary>附加消息处理过程到窗口</summary>
  60.         /// <param name="handle">需要附加消息处理过程的窗口句柄</param>
  61.         /// <param name="hook">消息处理过程</param>
  62.         /// <param name="removedHandler">消息处理过程移除回调</param>
  63.         public static void AddHook(IntPtr handle, HookProc hook, Action removedHandler = null)
  64.         {
  65.             if (!(NativeWindow.FromHandle(handle) is HookWindow window))
  66.             {
  67.                 window = new HookWindow(handle);
  68.             }
  69.             window.AddHookProc(hook, removedHandler);
  70.         }
  71.         /// <summary>从窗口移除附加的消息处理过程</summary>
  72.         /// <param name="handle">需要移除消息处理过程的窗口句柄</param>
  73.         /// <param name="hook">消息处理过程</param>
  74.         public static void RemoveHook(IntPtr handle, HookProc hook)
  75.         {
  76.             if (NativeWindow.FromHandle(handle) is HookWindow window)
  77.             {
  78.                 window.RemoveHookProc(hook);
  79.             }
  80.         }
  81.     }
  82. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

怀念夏天

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表