C# 窗口过程消息处理 WndProc
笔记哥 /
04-01 /
29点赞 /
0评论 /
480阅读
# C# 窗口过程消息处理 WndProc
# WinForm WndProc
在 WinForm 中一般采用重写 [WndProc](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.wndproc) 的方法对窗口或控件接受到的指定消息进行处理
>
>
> 示例:禁止通过关闭按钮或其他发送 WM\_CLOSE 消息的途径关闭窗口
>
```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);
}
```
Control 类中还有个 DefWndProc 为默认的窗口过程
# WPF HwndSource
WPF 仅本机窗口或 HwndHost 嵌入控件拥有句柄,可通过 [HwndSource](https://learn.microsoft.com/en-us/dotnet/api/system.windows.interop.hwndsource) 添加消息处理
>
>
> 示例:禁止通过关闭按钮或其他发送 WM\_CLOSE 消息的途径关闭窗口
>
```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;
}
```
# WinForm IMessageFilter
⚠ 注意:1.消息过滤器对于特定线程是唯一的;2.使用消息过滤器可能会降低程序性能
[IMessageFilter](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.imessagefilter) 接口允许程序在将消息调度到控件或窗口之前捕获消息进行预处理
IMessageFilter 的 PreFilterMessage 与 Control 的 WndProc 接收到的消息是一个交集关系,应用程序接收到的消息来自系统消息队列,相对来说更全,但会有部分消息会直接发送到窗口或控件而不进入系统消息队列
实现 IMessageFilter 接口实例可对整个线程消息循环进行预处理,并根据 m.HWnd 获取消息传入的窗口或控件句柄
>
>
> 示例:截获程序鼠标悬浮消息,窗口标题显示当前悬浮控件名
>
```csharp
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; // 允许消息派发到下一个过滤器或控件
}
}
```
# WinForm NativeWindow
[NativeWindow](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.nativewindow) 是 IWin32Window 的低级封装,并且和 WinForm Control 一样拥有 WndProc 和 DefWndProc 方法,故同样可通过重写 WndProc 方法处理消息
可以通过 CreateHandle(new CreateParams()) 创建没有 UI 的仅消息循环的窗口。比如托盘图标类 [NotifyIcon](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.notifyicon) 内部会创建一个 NativeWindow 用来接收任务栏创建消息 WM\_TASKBARCREATED ("TaskbarCreated"),在资源管理器崩溃重启后重新创建图标。
## 附加到其他窗口
由于 WinForm Control WndProc 是密封的,处理消息时必须继承类型并重写,需要单独进行消息处理的窗口或控件较多时,对原代码具有很大的侵入性;而 IMessageFilter 是针对整个应用程序的消息循环,官方文档说使用消息过滤器很可能会降低程序性能;相对来说,由于 HwndSource AddHook 和 RemoveHook 不是密封的,WPF 程序可以在不侵入原代码的条件下处理窗口消息,在可复用性上面反而还具有优势。但如果仔细看看 NativeWindow 源代码,会发现它内部调用了 [SetWindowLong](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlonga) GWL\_WNDPROC (窗口子类化),可以通过 AssignHandle 附加到任意窗口或控件进行消息处理,这个窗口不限制类型,甚至可以附加到其他程序窗口。
这里提供一个静态辅助类,借助 NativeWindow 简化附加窗口消息过程处理操作:
```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> hooks;
public HookWindow(IntPtr hWnd)
{
AssignHandle(hWnd);
}
public void AddHookProc(HookProc hook, Action removedHandler)
{
if (hooks == null)
{
hooks = new List>();
}
hooks.Insert(0, new KeyValuePair(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) // 窗口销毁时移除所有 hook
{
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);
}
}
}
}
```
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/1891
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利
- 相关联分享
- C# 窗口过程消息处理 WndProc