談到Winform的消息處理,多數時候是通過事件處理程序進行的,但當沒有對應的事件時通常的做法是聲明DefWndProc或者WndProc或者IMessageFilter,經常在網上看見有文章將三者並列,那么它們有什么區別呢?
DefWndProc和WndProc都是繼承自Control類中的虛方法,原型如下:
1: protected override void DefWndProc(ref Message m)
2: {
3: ....
4: base.DefWndProc(m);
5: }
6:
7: protected override void WndProc(ref Message m);
8: {
9: .....
10: base.WndProc(m);
11: }
所有的有用戶界面的控件都繼承自Control,這種方式需要創建對應控件的派生類,不能統一對各個窗口的消息進行攔截處理,因為從根本上說這兩者都是Windows的窗口過程,只有收到針對本窗口自身的消息。
通過復習Windows的消息處理機制,對這三者的關系可以有更好的理解。應用程序的消息來自於系統消息隊列,被應用程序的主程序中的消息循環所處理。這個消息循環從應用程序的消息隊列中取出消息,進行預處理,然后派發到消息對應的窗口過程,窗口過程在被調用后根據消息的類型進行相應的處理,有些可以由Windows默認處理的消息就調用Windows的DefWindowProc。
這里的WndProc就是對應控件窗口的窗口過程,而DefWndProc會被WndProc調用,處理那些WndProc中未處理的消息(包括WndProc未吞掉的),因此DefWndProc收到的消息會比WndProc少。
IMessageFilter的調用發生在應用程序的消息循環中,是消息預處理的一部分,所以它收到的消息是更全的(除了直接發送到窗口過程不進入消息隊列的那些消息)。使用方式如下:
1: public class MessageFilter : IMessageFilter
2: {
3: public bool PreFilterMessage(ref Message msg)
4: {
5: //識別消息並處理
6: //return true;//吞掉消息,不派發
7: return false;//進入下一步派發到對應窗口過程
8: }
9: }
10:
11: //在應用程序消息循環中加入消息過濾器
12: MessageFilter f = new MessageFilter(this.lbMsg);
13: Application.AddMessageFilter(f);
14:
三者都有一個共同的參數類型Message,它封裝了Windows消息。同時還包括一個很方便的ToString方法,可以將Message對象轉換成包括消息名稱(WM_XXX)在內的字符串,通過Reflector可以看到實現是通過一個內部類MessageDecoder,使用一個很長的switch語句將消息ID轉換成消息名稱。
Message的定義如下:
1: [StructLayout(LayoutKind.Sequential), SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
2: public struct Message
3: {
4: private IntPtr hWnd;
5: private int msg;
6: private IntPtr wparam;
7: private IntPtr lparam;
8: private IntPtr result;
9: public IntPtr HWnd { get; set; }
10: public int Msg { get; set; }
11: public IntPtr WParam { get; set; }
12: public IntPtr LParam { get; set; }
13: public IntPtr Result { get; set; }
14: public object GetLParam(Type cls);
15: public static Message Create(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);
16: public override bool Equals(object o);
17: public static bool operator !=(Message a, Message b);
18: public static bool operator ==(Message a, Message b);
19: public override int GetHashCode();
20: public override string ToString();
21: }
22:
其中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的輸出窗口監視到的調試輸出:
P.S.腦子里一直想着好像還有種定義消息處理過程的方式,而且是可以直接指定處理哪個消息,好像使用的關鍵字是“message”。。。
在MSDN上搜索N久后反應過來,哦,好象是Delphi中的方法;-)
原文地址:http://ymail2000.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=cat%3dWinform