天天看點

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的輸出視窗監視到的調試輸出如下圖所示: