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

首頁 > 編程 > .NET > 正文

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

2024-07-10 12:58:53
字體:
來源:轉載
供稿:網友
曾經在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(字符輸入)。

  何謂消息

  鼠標移動、按鍵被按下、窗口被關閉.,這些都會產生消息。在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)到畫面上,再設定組件屬性的工作,稱不上有太大的難度。只有深入了解內部原理,才能讓自己對技術融會貫通,也才能讓程序員之路走得更穩健、更長久。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 葫芦岛市| 龙山县| 松溪县| 金门县| 本溪市| 德钦县| 洛扎县| 泰和县| 泰兴市| 龙川县| 东辽县| 讷河市| 鄄城县| 红河县| 会宁县| 天镇县| 房产| 西贡区| 华池县| 枝江市| 武城县| 洛宁县| 克什克腾旗| 毕节市| 将乐县| 曲麻莱县| 县级市| 漾濞| 清河县| 浮梁县| 濮阳县| 旺苍县| 县级市| 临沂市| 会昌县| 丘北县| 缙云县| 凤庆县| 祁连县| 柳河县| 故城县|