国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

揭開.NET消息循環的神秘面紗

2019-11-17 04:46:46
字體:
來源:轉載
供稿:網友

  曾經在Win32平臺下奮戰的程序員們想必記得,為了弄清楚“消息循環”的概念,度過多少不眠之夜。盡管如今在應用程序代碼的編寫過程中,我們已經不再需要它,但是深刻理解Windows平臺內部的消息流轉機制依然必要..

  在早年直接用Win32/Win16 API寫程序的時代,消息循環是我們必須搞懂的第一個觀念。現在,不管你用是Windows上面的哪一套application Framework(MFC、VCL、VB、.NET Framework),甚至Unix、linux、MacOSX上面的Application Framework,都不太輕易看到消息循環。事實上,消息循環依然存在,只是被這些ApplicationFramework包裝起來,深深地埋藏在某個角落。

  本文章試圖喚起大家對于消息循環的回憶,也試圖解釋消息循環如何被封裝進.NET Framework的Windows Forms中。雖然Windows Forms將這一切都藏起來,但是也留下許多空間,讓我們可以自行處理Win32的消息。

  傳統的Windows 程序

  傳統的Windows程序,只利用Win32 API撰寫,下面是一個程序范例,為了節省篇幅,我將其中許多程序代碼省略:

// 程序進入點

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPRevInstance,

LPTSTR lpCmdLine, int nCmdShow){
 MSG msg;
 if (!InitInstance (hInstance, nCmdShow)){
  return FALSE;
 }

 // 主消息循環:

 while (GetMessage(&msg, NULL, 0, 0)){
  TranslateMessage(&msg); DispatchMessage(&msg);
 }
 return (int) msg.wParam;
}

// 函數: WndProc(HWND, unsigned, Word, LONG)
// 用途: 處理主窗口的消息。

LRESULT CALLBACK WndProc(
 HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  int wmId, wmEvent; PAINTSTRUCT ps;
  HDC hdc;
  switch (message){
   case WM_COMMAND:
    wmId = LOWORD(wParam);
    wmEvent = HIWORD(wParam);
    // 剖析菜單選取項目:
    switch (wmId){
     case IDM_ABOUT:
      DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX,hWnd, (DLGPROC)About);
      break;
     case IDM_EXIT:
      DestroyWindow(hWnd);
      break;
     default:
      return DefWindowProc(hWnd, message,wParam,lParam);
    }
    break;
   case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);

    // TODO: 在此加入任何繪圖程序代碼...
    EndPaint(hWnd, &ps);
    break;

   case WM_DESTROY:
    PostQuitMessage(0);
    break;
   default:
    return DefWindowProc(hWnd, message,wParam, lParam);
  }
  return 0;
 }

 // [關于] 方塊的消息處理例程。

 LRESULT CALLBACK About(HWND hDlg, UINT message,

 WPARAM wParam, LPARAM lParam){
  switch (message){
   case WM_INITDIALOG:
    return TRUE;
   case WM_COMMAND:
    if (LOWORD(wParam) == IDOK LOWORD(wParam) == IDCANCEL){
     EndDialog(hDlg, LOWORD(wParam));
     return TRUE;
    }
    break;
   }
   return FALSE;
  }
  1、從_tWinMain內,程序進入主消息循環;

  2、消息循環從消息隊列(Message Queue)中取得一個消息(透過調用GetMessage())。每個執行中的程序都有一個屬于自己的消息隊列;

  3、消息循環根據消息內容來決定消息應該送給哪個Windows Procedure(WndProc),.. 這就稱為消息分發(Message Dispatch)。通常“每一種”窗口或控件(control)都有一個Windows Procedure,來處理該種窗口/控件的行為;

  4、Windows Procedure根據消息內容來決定應該調用哪個函數(利用Switch/Case語法);..

  5、Windows Procedure處理完,控制權回到消息循環。繼續進行2、3、4、5的動作;

  6、當消息隊列為空的時候,GetMessage()無法取得任何消息,就會進入Idle(空閑)狀態,進入睡眠狀態(而不是Busy Waiting)。當消息隊列不再為空的時候,程序會自動醒過來,繼續進行2、3、4、5的動作;

  7、當取得的消息是WM_QUIT,GetMessage()就會得到0的返回值,因而離開消息循環,程序結束。程序會利用調用PostQuitMessage()來將WM_QUIT放置進消息隊列中,來造成稍后結束,而不會直接貿然跳離開循環來結束。

  雖名為隊列(queue),.. 但是消息隊列中的消息并非總是先進先出(First In First Out,FIFO),有一些特例:

  . 只要消息隊列中有WM_QUIT ,就會先取出WM_QUIT,導致程序結束。

  . 只有在沒有其它消息的時候,WM_PAINT 和WM_TIMER才會被取出。且多個WM_PAINT可能會被合并成一個,WM_TIMER也是如此。

  . 利用TranslateMessage()來處理消息,可能會造成新消息的產生。例如:TranslateMessage()可以辨識出WM_KEYDOWN(按鍵按下)加上WM_KEYUP(按鍵放開)就產生WM_CHAR(字符輸入)。
更多的請看:http://www.QQread.com/windows/2003/index.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)到畫面上,再設定組件屬性的工作,稱不上有太大的難度。只有深入了解內部原理,才能讓自己對技術融會貫通,也才能讓程序員之路走得更穩健、更長久。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 电白县| 油尖旺区| 广饶县| 海晏县| 界首市| 长岭县| 本溪市| 浠水县| 合江县| 房产| 石河子市| 固镇县| 苏尼特右旗| 若尔盖县| 潮安县| 高青县| 鄂托克旗| 临漳县| 龙州县| 进贤县| 封开县| 读书| 东辽县| 郑州市| 新津县| 闻喜县| 宝应县| 东明县| 应城市| 东方市| 新乡市| 启东市| 新巴尔虎左旗| 沭阳县| 民丰县| 呼伦贝尔市| 安泽县| 朝阳市| 惠东县| 台北市| 房产|