熱點推薦:
您现在的位置: 電腦知識網 >> 操作系統 >> Windows系統管理 >> 正文

剖析Windows的消息運行機制

2013-11-11 21:37:24  來源: Windows系統管理 

  一引言
  
  隨著Windows操作系統的不斷推廣眾多軟件開發包都提供有開發基於Windows平台應用軟件的功能雖然這些開發包不盡相同流行的有Visual C++Visual BasicDelphiC++ Builder 等多種但由這些不同語言開發的軟件有一點卻是相同的都是運行於Windows 操作平台都必須接受Windows 的運行機制作為Windows 操作系統靈魂的消息機制也就必然為眾多用不同語言開發的Windows操作系統下運行的應用程序所接受因此要編寫深入的Windows程序就必須對Windows的運行機制有很好的認識和理解本文下面將對Windows操作系統下的消息運行機制做較為深入的剖析
  
  二Windows事件驅動機制
  
  我們當中不少使用VCDelphi等作為開發語言的程序員是一步步從DOS下的BasicC++中走過來的而且大多在剛開始學習編程時也是先從DOS下的編程環境入手的因此在習慣了DOS下的過程驅動形式的順序程序設計方法後往往在向Windows下的開發環境轉型的過程中會對Windows所采取的事件驅動方式感到無法適應因為DOS和Windows這兩種操作系統的運行機制是截然不同的DOS下的任何程序都是使用順序的過程驅動的程序設計方法這種程序都有一個明顯的開始明顯的過程以及一個明顯的結束因此通過程序就能直接控制程序事件或過程的全部順序即使是在處理異常時處理過程也仍然是順序的過程驅動的結構而Windows的驅動方式則是事件驅動的即程序的流程不是由事件的順序來控制而是由事件的發生來控制所有的事件是無序的所為一個程序員在編寫程序時並不知道用戶會先按下哪個按紐也就不知道程序先觸發哪個消息因此我們的主要任務就是對正在開發的應用程序要發出的或要接收的消息進行排序和管理事件驅動程序設計是密切圍繞消息的產生與處理而展開的一條消息是關於發生的事件的消息
  
  三Windows的消息循環
  
  Windows操作系統為每一個正在運行的應用程序保持有一個消息隊列當有事件發生後Windows並不是將這個激發事件直接送給應用程序而是先將其翻譯成一個Windows消息然後再把這個消息加入到這個應用程序的消息隊列中去應用程序需要通過消息循環來接收這些消息在MFC中使用了對WinAPI進行了很好封裝的類庫雖然可以為編程提供一個面向對象的界面使Windows程序員能夠以面象對象的方式進行編程把那些進行SDK編程時最繁瑣的部分提供給程序員使之專注於功能的實現但是由於引入了很好的封裝特性使我們不能直接操縱部分核心代碼對於消息的循環和接收也只是通過類似於下面的消息映射予以很簡單的表示
  
  BEGIN_MESSAGE_MAP(CTEMMSView CFormView)
  //{{AFX_MSG_MAP(CTEMMSView)
  ON_WM_LBUTTONDOWN()
  ON_COMMAND(ID_OPENDATA OnOpenData)
  ON_WM_TIMER()
  ON_WM_PAINT()
  //}}AFX_MSG_MAP
  END_MESSAGE_MAP()
  
  雖然上述消息映射在編程過程中處理消息非常簡練方便但顯然是難於理解消息是如何參與循環和分發的因此有必要通過SDK(Software Developers Kit軟件開發工具箱)代碼深入到被MFC封裝的Windows編程的核心中來研究其具體是如何工作的在SDK編程中一般是在Windows應用程序的入口點WinMain函數中添加處理消息循環的代碼以檢索Windows送來的消息然後WinMain再把這些消息分配給相應的窗口函數並處理它們
  
  ……
  MSG msg; //定義消息名
  while (GetMessage (&msg NULL ))
  {
  TranslateMessage (&msg) ; //翻譯消息
  DispatchMessage (&msg) ; //撤去消息
  }
  return msgwParam ;
  
  上述幾句雖然簡單但卻是所有Windows程序的關鍵代碼擔負著獲取解釋和分發消息的任務下面就重點對其功能和作用進行分析
  
  MSG結構在頭文件中定義如下
  
  typedef struct tagMSG
  {
  HWND hwnd;
  UINT message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD time;
  POINT pt;
  } MSG *PMSG;
  
  其數據成員的具體意義如下
  
  hwnd消息將要發送到的那個窗口的句柄用這個參數可以決定讓哪個窗口接收消息
  message消息號它唯一標識了一種消息類型每種消息類型都在Windows文件進行了預定義
  wParam一個位的消息參數這個值的確切意義取決於消息本身
  lParam同上
  time消息放入消息隊列中的時間在這個域中寫入的並非當時日期而是從Windows啟動後所測量的時間值Windows用
  這個域來使用消息保持正確的順序
  pt消息放入消息隊列時的鼠標坐標
  
  消息循環以GetMessage調用開始它從消息隊列中取出一個消息該函數的四個參數可以有控制地獲取消息第一個參數指定要接收消息的MSG結構的地址第二個參數表示窗口句柄一般將其設置為空表示要獲取該應用程序創建的所有窗口的消息第三四參數用於指定消息范圍後面三個參數被設置為默認值用於接收發送到屬於這個應用程序的任何一個窗口的所有消息在接收到除WM_QUIT之外的任何一個消息後GetMessage()返回TRUE如果GetMessage收到一個WM_QUIT消息則返回FALSE以退出消息循環終止程序運行因此在接收到WM_QUIT之前帶有GetMessage()的消息循環可以一直循環下去當除WM_QUIT的消息用GetMessage讀入後首先要經過函數TranslateMessage()對其進行解釋但對大多數消息來說並不起什麼作用這裡起關鍵作用的是DispatchMessage()函數把由GetMessage獲取的Windows消息傳送給在MSG結構中為窗口所指定的窗口過程在消息處理函數處理完消息之後代碼又循環到開始去接收另一個消息這樣就完成了一個完整的消息循環
  
  由於Windows操作系統是一種非剝奪式多任務操作系統只有在應用程序主動交出CPU控制權後Windows才能把控制權交給其他應用程序在消息循環中一定要有能交出控制的系統函數才能實現協同式多任務操作能完成該功能的只有GetMessagePeekMessage和WaitMessage這三個函數如果在應用程序中長期不去調用這三個函數之一其他任務則無法執行GetMessage函數在找不到等待應用程序處理的消息時會自動交出控制權由Windows把CPU的控制權交給其他等待獲取控制權的應用程序由於任何Windows應用程序都含有一個消息循環這種隱式交出控制權的方式可以保證合並各個應用程序共享控制權一旦發往該應用程序的消息到達應用程序隊列即開始執行GetMessage語句的下一條語句使用GetMessage函數的消息循環在消息隊列中沒有消息時將等待如果需要可以利用這段時間進行I/O端口操作等耗時操作不過需要在消息循環中使用PeekMessage函數來代替GetMessage使用PeekMessage的方法同GetMessage類似下面是一段使用PeekMessage函數的消息循環的典型例子
  
  MSG msg;
  BOOL bDone=FALSE;
  do{
  if(PeekMessage(&msgNULLPM_REMOVE)){
  if(ssage==WM_QUIT)
  bDone=TRUE;
  else{
  TranslateMessage(&msg);
  DispatchMessage(&msg);
  }
  }
  //無消息處理進行長時間操作
  else{
  ……//長時間操作
  }
  }while(!bDone)
  ……
  
  無論應用程序消息隊列中是否有消息PeekMessage函數都立即返回如果希望等待新消息入隊可以利用無返回值的函數WaitMessage配合PeekMessage進行消息循環
  
  四對Windowds消息的處理
  
  窗口過程處理消息通常以switch語句開始對於它要處理的每一條消息ID都跟有一條case語句這在功能上同MFC的消息映射有些類似
  
  switch(uMsgId)
  {
  case WM_TIMER
  //對WM_TIMER定時器消息的處理過程
  return ;
  case WM_LBUTTONDOWN
  //對WM_ LBUTTONDOWN鼠標左鍵單擊消息的處理過程
  ruturn ;
  ……
  default:
  //其他消息由這個默認處理函數來處理
  return DefWindowProc(hwnduMsgIdwParamlParam);
  }
  
  在處理完消息後必須返回這很重要否則Windows將要不停地重試下去對於那些在程序中不准備處理的消息窗口過程會把它們都扔給DefWindowProc進行缺省處理而且還要返回那個函數的返回值在消息傳遞層次中可以認為DefWindowProc函數是最頂層的函數該函數發出WM_SYSCOMMAND消息由系統執行Windows環境中多數窗口所公用的各種通用操作如更新窗口的正文標題等等 在MFC下可以用下述部分代碼實現與上述SDK代碼相同的功能
  
  BEGIN_MESSAGE_MAP(CTEMMSView CFormView)
  //{{AFX_MSG_MAP(CTEMMSView)
  ON_WM_LBUTTONDOWN()
  ON_WM_TIMER()
  //}}AFX_MSG_MAP
  END_MESSAGE_MAP()
  
  小結Windows環境提供有非常豐富的系統資源在這個基礎上可以編制出能滿足各種各樣目標功能的應用系統要深入Windows編程就必須首先對Windows系統的運行機理有很好的認識本文僅針對Windows的一種重要運行機制消息機制作了較深入的剖析和闡述對培養在Windows下的編程思想有一定的幫助對某些相關問題的詳細論述可以參考MSDN在線幫助的SDK Reference部分
  
  
  

From:http://tw.wingwit.com/Article/os/xtgl/201311/8832.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.