58人参与 • 2025-04-24 • C#
在 winform 中一般采用重写 wndproc 的方法对窗口或控件接受到的指定消息进行处理
示例:禁止通过关闭按钮或其他发送 wm_close 消息的途径关闭窗口
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 仅本机窗口或 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) { // messagebox.show("禁止关闭此窗口"); 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) // 窗口销毁时移除所有 hook { for (int i = hooks.count - 1; i >= 0; i--) { hooks[i].value?.invoke(); } hooks = null; } base.wndproc(ref m); } } } /// <summary>附加消息处理过程到窗口</summary> /// <param name="handle">需要附加消息处理过程的窗口句柄</param> /// <param name="hook">消息处理过程</param> /// <param name="removedhandler">消息处理过程移除回调</param> 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); } /// <summary>从窗口移除附加的消息处理过程</summary> /// <param name="handle">需要移除消息处理过程的窗口句柄</param> /// <param name="hook">消息处理过程</param> public static void removehook(intptr handle, hookproc hook) { if (nativewindow.fromhandle(handle) is hookwindow window) { window.removehookproc(hook); } } } }
到此这篇关于c# 窗口过程消息处理 wndproc的方法详解的文章就介绍到这了,更多相关c# 消息处理 wndproc内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论