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

來源:互聯網
上載者:User

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

 

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

 

曾經在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(字元輸入)。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.