一般來說,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的消息處理機制有所幫助。
新聞熱點
疑難解答