揭開.NET訊息迴圈的神秘面紗(二)

來源:互聯網
上載者:User

本文轉自:http://www.cnblogs.com/sakiwer/archive/2009/12/15/1619281.html

 

-----------------------------------------------------------------------------------------------------------------------------------------------------

 

何謂訊息

  滑鼠移動、按鍵被按下、視窗被關閉.,這些都會產生訊息。在Windows作業系統中,訊息是以下面的資料結構存在的(定義在WinUser.h檔案中):..

typedef struct tagMSG { 
HWND hwnd; 
UINT message; 
WPARAM wParam; 
LPARAM lParam; 
DWORD time; 
POINT pt; 
} MSG;

訊息內有六個資訊,分別是:

. hwnd:視窗/控制項的唯一hwnd的編號。訊息迴圈會根據此資訊,將訊息送到正確目標。

. message:Windows預先定義的訊息種類的ID。

. wParam 與lParam:有些message本身需要攜帶更多的資訊,這些資訊就放在wParam與lParam中。

. time與pt:訊息發生當時的時間與滑鼠位置。

.NET Framework如何封裝訊息迴圈

.NET Framework的Windows Forms將訊息迴圈封裝起來,以方便我們使用。本節中所提到的類(class),都是屬於System.Windows.Forms名字空間(namespace)。

簡單歸納如下:訊息迴圈被封裝進了Application類的Run()靜態方法中;Windows Procedure被封裝進了NativeWindow 與Control 類中;個別的訊息處理動作被封裝進Control 類的OnXyz()(例如OnPaint())。我們可以覆蓋(override)OnXyz(),來提供我們自己的程式。也可以利用.NET的事件 (event)機制,在Xyz事件上,加入我們的事件處理函數(Event Handler)。Control類的OnXyz()會主動調用Xyz 事件的處理函數。

請注意,因為Xyz 的事件處理函數是由Control類的OnXyz()方法所調用的,所以當你覆寫OnXyz()方法時,不要忘了調用Control類的OnXyz() (除非你有特殊需求),否則Xyz事件處理函數將會沒有作用。只要調用base.OnXyz(),就可以調用到Control類的OnXyz()方法,如 下所示:

protected override void OnPaint(PaintEventArgs e){ 
base.OnPaint (e);// TODO: 加入 Form1.OnPaint 實作
}

我們可以利用覆寫Control類的OnXyz(),來決定該訊息發生時要做些什麼。同理,我們甚至可以覆寫Control與NativeWindow類的WndProc(),來定義Windows Procedure。

再次提醒你,因為OnXyz()系列方法是由Control類的WndProc()所調用的,所以當你覆寫WndProc()時,不要忘了調用 Control類的WndProc()(除非你有特殊需求),否則OnXyz()系列方法(以及Xyz事件處理函數)將會沒有作用。只要調用 base.WndProc(),就可以調用到Control類的WndProc(),如下所示:

protected override void WndProc(ref Message m){ 
base.WndProc (ref m);//TODO: 加入Form1.WndProc 實作
}

你可能也注意到了,WndProc()需要一個Message類的參數,這正是MSG被封裝成.NET版本的結果。

一個Windows Forms的範例

為了讓讀者更加瞭解實際的狀況,我用下面的執行個體範例作說明:

namespace WindowsApplication1{ 
/// Form1 的摘要描述。
public class Form1 : Form{ 
/// 設計工具所需的變數。
private Container components = null; 
public Form1(){ 
AutoScaleBaseSize = new Size(5, 15); 
ClientSize = new Size(292, 266); 
Name = "Form1"; 
Text = "Form1"; 
Paint += new PaintEventHandler(this.Form1_Paint); 
Paint += new PaintEventHandler(this.Form1_Paint2); 

/// 應用程式的主進入點。
[STAThread] 
static void Main(){ 
Application.Run(new Form1()); 

protected override void OnPaint(PaintEventArgs e){ 
base.OnPaint (e); // 2 

private void Form1_Paint(object sender, PaintEventArgs e){ 
// 3 

private void Form1_Paint2(object sender, PaintEventArgs e){ 
// 4 

protected override void WndProc(ref Message m){ 
base.WndProc (ref m); // 1 


}

1、在Main()中,利用Application.Run()來將Form1視窗顯示出來,並進入訊息迴圈。程式的執行過程中,Application.Run()一直未結束。

2、OS在此Process的訊息佇列內放進一個WM_PAINT訊息,好讓視窗被顯示出來。

3、WM_PAINT被Application.Run()內的訊息迴圈取出來,分發到WndProc()。由於多態(Polymorphism)的因 素,此次調用(invoke)到的WndProc()是屬於Form1的WndProc(),也就是上述程式中批註(comment)1的地方,而不是調 用到 Control.WndProc()。

4、在Form1.WndProc()的最後,有調用base.WndProc(),這實際上調用到Control.WndProc()。

5、Control.WndProc()從Message參數中得知此訊息是WM_PAINT,於是調用OnPaint()。由於多態的因素,此次調用 到的OnPaint()是屬於Form1的OnPaint(),也就是上述程式中批註2的地方,而不是調用到 Control.OnPaint()。

6、在Form1.OnPaint()的最後,有調用base.OnPaint(),這實際上調用到Control.OnPaint()。

7、我們曾經在Form1的建構函式(constructor)中將Form1_Paint()與Form1_Paint2()登記成為Paint事件處理函數
(Event Handler)。Control.OnPaint()會去依序去調用這兩個函數,也就是上述程式中批註3與4的地方。

幹嘛知道這麼多?拜工具之賜,現在的程式員很幸福,可以在糊裡胡塗的情況下寫出程式來。不過這樣的程式員恐怕競爭力不強,畢竟將組件 (component)拖放(drag and drop)到畫面上,再設定組件屬性的工作,稱不上有太大的難度。只有深入瞭解內部原理,才能讓自己對技術融會貫通,也才能讓程式員之路走得更穩健、更長 久。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.