一般来说,Winform的消息处理机制多数时候是通过事件处理程序进行的,但当没有对应的事件时通常的做法是声明DefWndProc或者WndProc或者IMessageFilter,经常在网上看见有文章将三者并列,那么它们有什么区别呢?本文对此做一简单分析如下:
DefWndProc和WndProc都是继承自Control类中的虚方法,其原型如下:
protected override void DefWndProc(ref Message m) { .... base.DefWndProc(m); } protected override void WndProc(ref Message m); { ..... base.WndProc(m); }
所有的有用户界面的控件都继承自Control,这种方式需要创建对应控件的派生类,不能统一对各个窗口的消息进行拦截处理,因为从根本上说这两者都是Windows的窗口过程,只有收到针对本窗口自身的消息。
通过复习Windows的消息处理机制,对这三者的关系可以有更好的理解。应用程序的消息来自于系统消息队列,被应用程序的主程序中的消息循环所处理。这个消息循环从应用程序的消息队列中取出消息,进行预处理,然后派发到消息对应的窗口过程,窗口过程在被调用后根据消息的类型进行相应的处理,有些可以由Windows默认处理的消息就调用Windows的DefWindowProc。
这里的WndProc就是对应控件窗口的窗口过程,而DefWndProc会被WndProc调用,处理那些WndProc中未处理的消息(包括WndProc未吞掉的),因此DefWndProc收到的消息会比WndProc少。
IMessageFilter的调用发生在应用程序的消息循环中,是消息预处理的一部分,所以它收到的消息是更全的(除了直接发送到窗口过程不进入消息队列的那些消息)。使用方式如下:
public class MessageFilter : IMessageFilter { public bool PreFilterMessage(ref Message msg) { //识别消息并处理 //return true;//吞掉消息,不派发 return false;//进入下一步派发到对应窗口过程 } } //在应用程序消息循环中加入消息过滤器 MessageFilter f = new MessageFilter(this.lbMsg); Application.AddMessageFilter(f);
三者都有一个共同的参数类型Message,它封装了Windows消息。同时还包括一个很方便的ToString方法,可以将Message对象转换成包括消息名称(WM_XXX)在内的字符串,通过Reflector可以看到实现是通过一个内部类MessageDecoder,使用一个很长的switch语句将消息ID转换成消息名称。
Message的定义如下:
[StructLayout(LayoutKind.Sequential), SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] public struct Message { private IntPtr hWnd; private int msg; private IntPtr wparam; private IntPtr lparam; private IntPtr result; public IntPtr HWnd { get; set; } public int Msg { get; set; } public IntPtr WParam { get; set; } public IntPtr LParam { get; set; } public IntPtr Result { get; set; } public object GetLParam(Type cls); public static Message Create(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam); public override bool Equals(object o); public static bool operator !=(Message a, Message b); public static bool operator ==(Message a, Message b); public override int GetHashCode(); public override string ToString(); }
其中hWnd是消息对应的窗口句柄,根据上面的分析可以知道在窗口过程(DefWndProc,WndProc)中收到的窗口句柄都是该窗口的句柄,而在PreFilterMessage中收到的消息的窗口句柄则根据触发消息的窗口不同而不同。
在PreFilterMessage中收到消息时,可以使用Control.FromHandle得到窗口对应的控件对象,原型如下:
//Declaring Type: System.Windows.Forms.Control //Assembly: System.Windows.Forms, Version=2.0.0.0 public static Control FromHandle(IntPtr handle);通过这种方式可以监测各消息的信息来自哪个控件。 public bool PreFilterMessage(ref Message msg) { Control c = Control.FromHandle(msg.HWnd); if (c == null) System.Diagnostics.Debug.WriteLine("Filter:NULL" +"-" + msg.ToString()); else System.Diagnostics.Debug.WriteLine("Filter:" +c.Name+"-"+ msg.ToString()); return false; }
从Visual Studio的输出窗口监视到的调试输出如下图所示:
希望本文所述分析对大家深入理解WinForm的消息处理机制有所帮助。
本文向大家介绍java中&与&&的区别,包括了java中&与&&的区别的使用技巧和注意事项,需要的朋友参考一下 在java中&和&&都属于逻辑运算符,都是判断两边条件为真时为真,否则则为假 在程序中&的使用: 这一段代码运行结果为:5 也就是说&逻辑运算符实际上是会吧前后两个判断条件都会执行两次,i自加了两次,出现结果为:5 这一段代码运行结果为:4 也就是说&&逻辑运算符实际上只判断了前边一个条
本文向大家介绍C#中equal与==的区别相关面试题,主要包含被问及C#中equal与==的区别时的应答技巧和注意事项,需要的朋友参考一下 答: C#中,判断相等有两种方式,一种是传统的==操作,一种是object提供的Equals方法。 二者的区别在于: 一、==操作符判断的是堆栈中的值,Equlas判断的是堆中的值 C#提供值类型和引用类型,值类型存储在栈上,故用==判断是直接判断其值是否相等
本文向大家介绍title与h1的区别、b与strong的区别、i与em的区别?相关面试题,主要包含被问及title与h1的区别、b与strong的区别、i与em的区别?时的应答技巧和注意事项,需要的朋友参考一下 title 是 网页标题标签 h1 是文本标签 默认与word的h1相似为最大的标题显示 b 为文本加粗标签 与word的加粗功能相似 strong 为强调语气, 也是为文本加粗 与b标签
本文向大家介绍c#中(&&,||)与(&,|)的区别详解,包括了c#中(&&,||)与(&,|)的区别详解的使用技巧和注意事项,需要的朋友参考一下 对于(&&,||),运算的对象是逻辑值,也就是True/False &&相当与中文的并且,||相当于中文的或者 。(叫做逻辑运算符又叫短路运算符) 运算结果只有下列四种情况。 True && True = True (左边为true,再验证右
本文向大家介绍浅析Javascript中“==”与“===”的区别,包括了浅析Javascript中“==”与“===”的区别的使用技巧和注意事项,需要的朋友参考一下 之前使用JavaScript的时候,时不时的会遇到使用 == 和 === 这两个符号来判断两个变量是否相等。但是对于这两个符号的差别一直没有去调查。今天又遇到了 === 这个符号,所以决定去查一下,究竟这两者有什么区别。 在Java
有人能给我解释一下在ElasticSearch中must_not和filter有什么区别吗? 例如。这里(摘自elasticsearch权威指南),为什么must_not不也用于范围? 具体看一下这篇文档,我觉得它们是完全一样的: 筛选器:子句(查询)必须出现在匹配的文档中。但是,与must不同的是,查询的分数将被忽略。筛选子句在筛选上下文中执行,这意味着计分被忽略,子句被考虑用于缓存。 must