[譯]理解Windows訊息迴圈

來源:互聯網
上載者:User
理解訊息迴圈和整個訊息傳送機制對Windows編程來說非常重要。如果對訊息處理的整個過程不瞭解,在windows編程中會遇到很多令人困惑的地方。

什麼是訊息(Message)

每個訊息是一個整型數值,如果查看標頭檔(查看標頭檔瞭解API是一個非常好的習慣和普遍的做法)可以發現如下一些宏定義:#define WM_INITDIALOG                   0x0110
#define WM_COMMAND                      0x0111

#define WM_LBUTTONDOWN                  0x0201
//...

在Windows通訊中,至少一些基本Windows通訊,幾乎都要用到訊息。如果你想讓視窗或控制項(實質上,控制項是特殊的視窗)執行何種動作,你應該傳送一個訊息給它;如果另一個視窗想讓你執行何種操作,它可以傳送一個訊息給你。如果一個事件,如敲擊鍵盤、移動滑鼠、點擊按鈕等,系統將訊息傳送給視窗,如果你是這些視窗之一,你將接收到訊息執行相應的操作。

每個Windows訊息共有兩個參數,wParam和lParam。最初的wParam是16位(Win16時代)的,lParam是32位的。在Win32中,兩個參數都是32位的。並不是所有的訊息都是用這兩個參數,每個訊息使用它們的方式也不盡相同。如WM_CLOSE訊息會忽略上述兩個參數;再如WM_COMMAND訊息使用上述兩個參數,wParam包含”兩個”值,HIWORD(wParam)是通知資訊(如果可用),LOWORD(wParam)是發送訊息的控制項或菜單的ID,lParam是發送訊息的控制項的HWND(視窗控制代碼),如果這個值為NULL,表示這個訊息不是由控制項發送的。

HIWORD()和LOWORD()是Windows定義的宏,分別取出一個32位整型值的高字和低字。在Win32中,一個”字”是一個16位整型,DWORD(Double WORD)是32位整型。

可以用PostMessage()或SendMessage()發送訊息。PostMessage()把一個訊息放入訊息佇列(Message Queue)後立即返回,也就是當調用PostMessage(),函數執行完成返回時,很可能訊息尚未處理。SendMessage()直接將訊息發送到視窗,直到這個訊息處理完成才返回。如果要關閉一個視窗,可以給它發送一個WM_CLOSE訊息,像PostMessage(hwnd, WM_CLOSE, 0, 0); 效果跟點擊視窗右上方的(關閉)按鈕是一樣的。注意這裡的wParam和lParam的值都是0,因為前面提到過,WM_CLOSE訊息會忽略上述兩個參數。

對話方塊(Dialogs)

如果使用對話方塊,為跟控制項通訊,你需要向控制項發送訊息。你或者可以使用GetDlgItem()函數根據控制項的ID取得控制項的控制代碼,然後調用SendMessage()函數發送訊息;或者使用SendDlgItemMessage()組合了上面的步驟。傳入一個視窗控制代碼和子控制項的ID能夠取得子控制項的控制代碼,用這個控制代碼發送訊息。跟SendDlgItemMessage()類似的API如GetDlgItemText()能夠對所有的視窗進行操作,而不僅僅是對話方塊。

什麼是訊息佇列(Message Queue)

假設一個情境:系統正在處理WM_PAINT訊息,就在這時使用者在鍵盤上敲擊了一些按鍵,這時會發生什麼呢?系統應該中斷繪圖操作然後處理按鍵訊息還是應該丟棄按鍵的訊息?很明顯這些都是不合理的,因此我們引入了訊息佇列,當訊息發送過來,將訊息加入訊息佇列,當一個訊息被處理時,將其從訊息佇列移除。這樣確保訊息不會丟失,當你正在處理一個訊息時,其它到來的訊息可以加入到訊息佇列直到被處理。

什麼是訊息迴圈(Message Loop)

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
}

上面代碼的執行過程為:
1. 訊息迴圈調用GetMessage()從訊息佇列中尋找訊息進行處理,如果訊息佇列為空白,程式將停止執行並等待(程式阻塞)。
2. 事件發生時導致一個訊息加入到訊息佇列(例如系統註冊了一個滑鼠點擊事件),GetMessage()將返回一個正值,這表明有訊息需要被處理,並且訊息已經填充到傳入的MSG參數中;當傳入WM_QUIT訊息時返回0;如果傳回值為負表明發生了錯誤。
3. 取出訊息(在Msg變數中)並將其傳遞給TranslateMessage()函數,這個函數做一些額外的處理:將虛擬索引值資訊轉換為字元資訊。這一步實際上是可選的,但有些地方需要用到這一步。
4. 上面的步驟執行完後,將訊息傳遞給DispatchMessage()函數。DispatchMessage()函數將訊息分發到訊息的目標視窗,並且尋找目標視窗過程函數,給視窗過程函數傳遞視窗控制代碼、訊息、wParam、lParam等參數然後調用該函數。
5. 在視窗過程函數中,檢查訊息和其他參數,你可以用它來實現你想要的操作。如果不想處理某些特殊的訊息,你應該總是調用DefWindowProc()函數,系統將按按預設的方式處理這些訊息(通常認為是不做任何操作)。
6. 一旦一個訊息處理完成,視窗過程函數返回,DispatchMessage()函數返回,繼續迴圈處理下一個訊息。

訊息迴圈對Windows編程來說是一個非常重要的概念。視窗過程函數並不是系統自動調用的,而是由開發人員自己通過調用DispatchMessage()間接的調用的。如果你願意,可以調用GetWindowLong()函數通過視窗控制代碼尋找到視窗過程函數直接調用達到訊息處理的目的。

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
    WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
    fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}

我嘗試著寫了上面的代碼,它確實能工作,但這裡存在各種問題,像Unicode/ANSI編碼轉換、定時器回調等等都這樣的代碼都不適合,並且很可能導致很多打斷很多程式的正常運行。因此這樣的代碼在這裡僅僅是實驗,真實項目中一定不能編寫這樣的代碼。

注意這裡我們用GetWindowLong()來獲得相關視窗的視窗過程函數。為什麼我們不直接調用WndProc()函數呢?訊息迴圈會處理常式中所有視窗的訊息,包括像按鈕、列表框等有他們自己的視窗過程函數的控制項,因此我們要保證調用正確的視窗過程函數。儘管有時幾個視窗調用同一個視窗過程函數,但函數的第一個參數 (視窗的控制代碼) 通常用於告知視窗過程函數是那個視窗發送的訊息。

代碼可以看出,程式的大部分時間都在處理訊息迴圈。視窗會不斷的處理髮過來的訊息,但如果要退出程式該怎麼做呢?因為我們用的是while()迴圈,如果GetMessage()返回的是FALSE(即0)會退出迴圈,程式能夠執行到WinMain()結束處,即程式退出:這正是PostQuitMessage()函數完成的工作,該函數會將WM_QUIT訊息添加到訊息佇列的隊尾,GetMessage()從訊息佇列取出WM_QUIT訊息,填充Msg結構,返回的不是正數,而是0。與此同時,結構Msg的成員wParam的值會被置為你傳給PostQuitMessage()函數參數的值,你可以選擇忽略它或做為WinMain()函數的傳回值即進程的結束代碼(Exit Code)。

注意:如果發生錯誤,GetMessage()函數將返回-1。你應該記住這點,說不定你的程式會因此出錯。儘管GetMessage()傳回值位BOOL型,但它可以返回TRUE或FALSE之外的值,因為BOOL被定義成UINT(unsigned int)。下面的程式貌似能正常工作,但有些時候不能正常工作。

while(GetMessage(&Msg, NULL, 0, 0))

while(GetMessage(&Msg, NULL, 0, 0) != 0)

while(GetMessage(&Msg, NULL, 0, 0) == TRUE)

上面的代碼都是錯誤的!有些程式中你會看到會使用第一中方式,使用這種方式你必須保證GetMessage()總是執行成功,否則應該使用下面這段代碼:while(GetMessage(&Msg, NULL, 0, 0) > 0)

希望你對Windows訊息迴圈能有很好的理解,如果還沒有,慢慢來,在使用過程中會逐漸理解的。

英文原文:http://winprog.org/tutorial/message_loop.html

譯註:感覺這篇文章對訊息迴圈講述的不錯。如果有翻譯的疏漏之處請指出。

相關文章

聯繫我們

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