法律人老王 發表於 2025-4-1 09:04:00

C# 窗口过程消息处理 WndProc

<h2 id="winformwndproc">WinForm&nbsp;WndProc</h2>
<p>在&nbsp;WinForm&nbsp;中一般采用重写&nbsp;WndProc&nbsp;的方法对窗口或控件接受到的指定消息进行处理</p>
<details open="">
<summary>示例:禁止通过关闭按钮或其他发送&nbsp;WM_CLOSE&nbsp;消息的途径关闭窗口</summary>
<pre><code class="language-csharp">protected override void WndProc(ref Message m)
{
    const int WM_CLOSE = 0x0010;
    if(m.Msg == WM_CLOSE)
    {
      // MessageBox.Show("禁止关闭此窗口");
      return;
    }
    base.WndProc(ref m);
}
</code></pre>
</details>
<p>Control&nbsp;类中还有个&nbsp;DefWndProc&nbsp;为默认的窗口过程</p>
<h2 id="wpfhwndsource">WPF&nbsp;HwndSource</h2>
<p>WPF&nbsp;仅本机窗口或&nbsp;HwndHost&nbsp;嵌入控件拥有句柄,可通过&nbsp;HwndSource&nbsp;添加消息处理</p>
<details open="">
<summary>示例:禁止通过关闭按钮或其他发送&nbsp;WM_CLOSE&nbsp;消息的途径关闭窗口</summary>
<pre><code class="language-csharp">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)
    {
      // MessageBox.Show("禁止关闭此窗口");
      handled = true; // 标记为已处理
    }
    return IntPtr.Zero;
}
</code></pre>
</details>
<h2 id="winformimessagefilter">WinForm&nbsp;IMessageFilter</h2>
<p>⚠&nbsp;注意:1.消息过滤器对于特定线程是唯一的;2.使用消息过滤器可能会降低程序性能</p>
<p>IMessageFilter&nbsp;接口允许程序在将消息调度到控件或窗口之前捕获消息进行预处理</p>
<p>IMessageFilter&nbsp;的&nbsp;PreFilterMessage&nbsp;与&nbsp;Control&nbsp;的&nbsp;WndProc&nbsp;接收到的消息是一个交集关系,应用程序接收到的消息来自系统消息队列,相对来说更全,但会有部分消息会直接发送到窗口或控件而不进入系统消息队列</p>
<p>实现&nbsp;IMessageFilter&nbsp;接口实例可对整个线程消息循环进行预处理,并根据&nbsp;m.HWnd&nbsp;获取消息传入的窗口或控件句柄</p>
<details open="">
<summary>示例:截获程序鼠标悬浮消息,窗口标题显示当前悬浮控件名</summary>
<pre><code class="language-csharp">static class Program
{
   
    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 &amp;&amp; Control.FromHandle(m.HWnd) is Control ctr)
      {
            ctr.FindForm().Text = ctr.Name;
            return true; // 过滤消息不继续派发
      }
      return false; // 允许消息派发到下一个过滤器或控件
    }
}
</code></pre>
</details>
<h2 id="winformnativewindow">WinForm&nbsp;NativeWindow</h2>
<p>NativeWindow&nbsp;是&nbsp;IWin32Window&nbsp;的低级封装,并且和&nbsp;WinForm&nbsp;Control&nbsp;一样拥有&nbsp;WndProc&nbsp;和&nbsp;DefWndProc&nbsp;方法,故同样可通过重写&nbsp;WndProc&nbsp;方法处理消息</p>
<p>可以通过&nbsp;CreateHandle(new&nbsp;CreateParams())&nbsp;创建没有&nbsp;UI&nbsp;的仅消息循环的窗口。比如托盘图标类&nbsp;NotifyIcon&nbsp;内部会创建一个&nbsp;NativeWindow&nbsp;用来接收任务栏创建消息&nbsp;WM_TASKBARCREATED&nbsp;("TaskbarCreated"),在资源管理器崩溃重启后重新创建图标。</p>
<h3 id="附加到其他窗口">附加到其他窗口</h3>
<p>由于&nbsp;WinForm&nbsp;Control&nbsp;WndProc&nbsp;是密封的,处理消息时必须继承类型并重写,需要单独进行消息处理的窗口或控件较多时,对原代码具有很大的侵入性;而&nbsp;IMessageFilter&nbsp;是针对整个应用程序的消息循环,官方文档说使用消息过滤器很可能会降低程序性能;相对来说,由于&nbsp;HwndSource&nbsp;AddHook&nbsp;和&nbsp;RemoveHook&nbsp;不是密封的,WPF&nbsp;程序可以在不侵入原代码的条件下处理窗口消息,在可复用性上面反而还具有优势。但如果仔细看看&nbsp;NativeWindow&nbsp;源代码,会发现它内部调用了&nbsp;SetWindowLong&nbsp;GWL_WNDPROC&nbsp;(窗口子类化),可以通过&nbsp;AssignHandle&nbsp;附加到任意窗口或控件进行消息处理,这个窗口不限制类型,甚至可以附加到其他程序窗口。</p>
<details open="">
<summary>这里提供一个静态辅助类,借助&nbsp;NativeWindow&nbsp;简化附加窗口消息过程处理操作</summary>
<pre><code class="language-csharp">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&lt;KeyValuePair&lt;HookProc, Action&gt;&gt; hooks;

            public HookWindow(IntPtr hWnd)
            {
                AssignHandle(hWnd);
            }

            public void AddHookProc(HookProc hook, Action removedHandler)
            {
                if (hooks == null)
                {
                  hooks = new List&lt;KeyValuePair&lt;HookProc, Action&gt;&gt;();
                }
                hooks.Insert(0, new KeyValuePair&lt;HookProc, Action&gt;(hook, removedHandler));
            }

            public void RemoveHookProc(HookProc hook)
            {
                if (hooks != null)
                {
                  for (int i = hooks.Count - 1; i &gt;= 0; i--)
                  {
                        if (hooks.Key == hook)
                        {
                            hooks.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) // 窗口销毁时移除所有 hook
                  {
                        for (int i = hooks.Count - 1; i &gt;= 0; i--)
                        {
                            hooks.Value?.Invoke();
                        }
                        hooks = null;
                  }
                  base.WndProc(ref m);
                }
            }
      }

      /// &lt;summary&gt;附加消息处理过程到窗口&lt;/summary&gt;
      /// &lt;param name="handle"&gt;需要附加消息处理过程的窗口句柄&lt;/param&gt;
      /// &lt;param name="hook"&gt;消息处理过程&lt;/param&gt;
      /// &lt;param name="removedHandler"&gt;消息处理过程移除回调&lt;/param&gt;
      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);
      }

      /// &lt;summary&gt;从窗口移除附加的消息处理过程&lt;/summary&gt;
      /// &lt;param name="handle"&gt;需要移除消息处理过程的窗口句柄&lt;/param&gt;
      /// &lt;param name="hook"&gt;消息处理过程&lt;/param&gt;
      public static void RemoveHook(IntPtr handle, HookProc hook)
      {
            if (NativeWindow.FromHandle(handle) is HookWindow window)
            {
                window.RemoveHookProc(hook);
            }
      }
    }
}
</code></pre>
</details><br><br>
来源:https://www.cnblogs.com/BluePointLilac/p/18802906
頁: [1]
查看完整版本: C# 窗口过程消息处理 WndProc