Win32編程API 基礎篇 -- 4.消息循環


消息循環

理解消息循環

  為了編寫任何即使是最簡單的程序,了解windows程序的消息循環和整個消息發送結構是非常有必要的。既然我們已經嘗試了一點消息處理的東西,我們應該對整個程序有更深入的理解,如果你沒有理解消息是怎么發生的和它們運行的機制,那接下來的內容你會感到很蛋疼。

 

什么是消息?

  一條消息是一個整數值,如果你查閱你的頭文件(這是個好的查閱API的工作慣例)你會發現像下面的東西:

 

1 #define WM_INITDIALOG                   0x0110
2 #define WM_COMMAND                      0x0111
3 
4 #define WM_LBUTTONDOWN                  0x0201

 

  還有很多類似的東西。在windows中消息幾乎被運用在基本水平上的所有通信,如果你想要一個窗口控制(只是一個制定的窗口)做某些事情你需要發送給它一條消息;同樣的如果另一個窗口想讓你做某些事情,它會發送給你一條消息;如果某些像用戶點擊鍵盤、移動鼠標、點擊按鈕的事件發生,那么相應的消息會被系統發送到受影響的窗口,如果你是其中一個受影響的窗口,那么你需要處理消息並采取相應的行動。

  每條窗口消息都可能有兩個參數,wParam和lParam,最初wParam是16位而lParam是32位,但是在win32中它們都是32位。不是每條消息都需要用到這些參數,用到時的用法也不同,舉個例子,WM_CLOSE消息就都沒有用到這兩個參數,這時我們可以忽略這兩個采納數;而WM_COMMAND就同時用到了這兩個參數,wParam包含了兩個值,HIWORD(wParam)是一條通知消息(如果可用的話),LOWORD(wParam)是一個發送消息的控制或菜單id。

  lParam是控制發送消息或NULL(如果消息不是從控制中來的話)的HWND(窗口句柄)

  HIWORD()和LOWORD()被windows宏定義,這兩個值都是windows中的宏定義,一個指32位(0xFFFF0000)的高16位(FFFF),一個指32位的低16位(0000);在win32中一個WORD表示的是16位,而DWORD(double word)才表示32位

  你可以通過PostMessage()或SendMessage()這兩種方式發送消息。PostMessage()將消息插入到消息隊列中然后馬上返回,這意味着即使一次PostMessage()的調用被完成時,消息可能還沒被處理;SendMessage()直接發送消息給窗口並且會不會立即返回,知道窗口完成了對消息的處理。如果我們想關閉一個窗口,我們可以通過PostMessage(hwnd, WM_CLOSE, 0, 0)發送一個WM_CLOSE的消息,這跟我們點擊窗口右上方的X按鈕有同樣的效果,注意在WM_CLOSE消息的相應處理中,wParam和lParam都是0,這是因為剛才提到的在WM_CLOSE消息處理中沒有用到這兩個參數。

 

 

對話框

 

  一旦你開始使用對話框盒子,為了跟它們交流你將需要發送消息給控制。你可以通過使用GetDlgItem()通過使用ID先獲取控制的句柄然后使用SendMessage(),或者你可以使用SendDlgItemMessage(),這個方法組合了上面的兩個步驟。你給它一個窗口句柄和孩子ID將會得到一個孩子句柄,然后向他發送消息。SendDlgItemMessage()和類似的APIGetDlgItemText()將會作用在所有的窗口上,而不僅僅是對話框。

 

 

什么是消息隊列?

  讓我們試着想象當你忙着處理WM_PAINT的時候突然用戶用鍵盤輸入一堆東西,會發生什么事情?你繪圖的時候被鍵盤打斷應該拋棄嗎?當然不是!很明顯不管拋棄哪個都不是正確的做法,所以我們有了消息隊列。新增POST過來的消息會被添加到消息隊列中來,當消息被處理時消息就會從消息隊列中被移除,這確保了你不會錯過一條消息,如果你正在處理一條消息,那么其他消息就會在隊列中等待直到你開始處理它們。

 

 

什么是一個消息環?

1 while(GetMessage(&Msg, NULL, 0, 0) > 0) 2 { 3     TranslateMessage(&Msg); 4     DispatchMessage(&Msg); 5 }

1、在消息隊列里,消息環調用GetMessage()方法,如果你的消息隊列空了,那么你的程序

   將會停止然后等待消息,相當於掛起的狀態

2、當一個事件發生時,會導致一條消息加入到消息隊列中(比如系統注冊一個鼠標點擊)

   GetMessage()返回一個正值表明有一個消息要處理,並且復制給我們傳遞給它的MSG數

   據結構的成員,方法也可能會返回0如果消息是WM_QUIT,如果是發生錯誤的話方法會

   返回負數。

3、我們得到消息(在Msg變量中)然后把它傳遞給TranslateMessage(),這會產生一點額外

   的處理,將虛擬的鍵盤點擊翻譯成字符消息,這個步驟實際上是可選的,如果不需要時

   它不會發生。

4、一旦上面的步驟完成,我們把消息傳遞給DispatchMessage(),DispatchMessage()會做的

   工作就是拿到消息,檢查它是哪個窗口的然后找到對應窗口的消息處理程序,接着調用

   消息處理程序,並且將窗口的句柄,消息,wParam和lParam四個參數傳遞給它。

5、在你的消息處理程序中,你會檢查參數消息,然后對它做任何你想做的事情,如果你沒

   有處理指定的消息,那么默認它會調用DefWindowProc()方法,這個消息處理的默認操作,

   通常意味着什么都不做。

6、一旦你已經完成了消息處理,你的消息處理程序會返回,DisPatchMessage()返回,然后

   我們又回到了環開始的地方。

 

  這是窗口程序中一個非常重要的概念,你的消息處理程序不是神奇地由系統調用,實際上你是間接地通過DispatchMessage()調用。如果你想要的話,你可以在窗口句柄中使用GetWindowLong()直接調用窗口處理程序。

 

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

 

  我試着用前面的代碼,但是並沒有正常工作,因為有各種各樣的問題比如Unicode/Ansi編碼翻譯等,調用定時器回調方法不會占用,這可能會打破我們目前的所有但微不足道的程序,所以你可以嘗試,但在實際的代碼中不要真的這么做: )

  注意,我們使用GetWindowLong()來檢索窗口相關的消息處理程序,為什么我們不直接調用WndProc()呢?嗯,我們的消息環是要對我們程序中所有窗口都適用的,包括有自己的窗口過程的按鈕和列表框,所以我們應該保證調用了正確的窗口過程,因為多個窗口可以使用同一個窗口過程,所以第一個參數(窗口句柄)用來告訴窗口過程消息屬於哪個窗口的。

  正如你所看到的,你的應用程序的大多數時間都花費在執行這個消息循環,你可以愉悅地發送消息給可以處理它們的快樂的窗口。但是當你想退出程序時你會怎么做?因為我們使用了一個while()循環,如果GetMessage()返回了false,循環將會結束並且到達我們WinMain()的終點接着退出程序。這正是PostQuitMessage()完成的東西,它把WM_QUIT消息放入隊列中而不是返回一個正值,GetMessage()會填充Msg數據結構的內容然后返回0,在這個時候Msg的wParam成員就包含了你傳遞給PostQuitMessage()的值,這里你可以忽略它,從WinMain()的返回值將會使用在進程結束的退出代碼中。

重點:GetMessgae()將會返回-1如果出現錯誤的話,確保記住這個,否則在某些時候會讓你很蛋疼。。。即使GetMessage()被定義用來返回一個布爾值,它也能夠返回TRUE或FALSE之外的值,因為BOOL使用UINT(unsigned int)來定義的。下面的代碼例子可能可以運行,但是不會正確地處理一下條件:

 

1     while(GetMessage(&Msg, NULL, 0, 0)) 2 
3     while(GetMessage(&Msg, NULL, 0, 0) != 0) 4 
5     while(GetMessage(&Msg, NULL, 0, 0) == TRUE)

 

  上面都是錯的!就像我剛剛提到的,它可能跟我第一個教程里使用的差不多,只要GetMessage()沒有失敗它會工作地很好,即使你的代碼是正確的,這個也不是正確的,但是在很多情況下這個代碼是錯誤的不能正常工作,當GetMessage()發生失敗的時候!這里我們鄭重說明和糾正,請原諒我錯過了一些要點。

 

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

 

  這個才是正確的!

  我希望現在你對窗口消息循環有更好的理解,如果還不是非常理解的話,別害怕,一旦你使用它們一段時間之后情況會好很多。

  PS.由於本人英文水平所限,只能翻譯到這個程度了,有紕漏還望多多指出,附上本篇翻譯的英文原版教程地址:http://www.winprog.org/tutorial/message_loop.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM