天天看点

WinForm中DefWndProc、WndProc与IMessageFilter的区别

一般来说,Winform的消息处理机制多数时候是通过事件处理程序进行的,但当没有对应的事件时通常的做法是声明DefWndProc或者WndProc或者IMessageFilter,经常在网上看见有文章将三者并列,那么它们有什么区别呢?本文对此做一简单分析如下:

DefWndProc和WndProc都是继承自Control类中的虚方法,其原型如下:

1

2

3

4

5

6

7

8

9

10

11

​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的调用发生在应用程序的消息循环中,是消息预处理的一部分,所以它收到的消息是更全的(除了直接发送到窗口过程不进入消息队列的那些消息)。使用方式如下:

12

13

​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的定义如下:

14

15

16

17

18

19

20

21

22

​[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的输出窗口监视到的调试输出如下图所示: