Window消息傳遞機制

Window消息傳遞機制
2007-03-10 23:12
MFC將thread分成winddow thread和worker thread,在討論多現程(Multi-thread)之前,我們先只考慮window thread。

     windows programming的基本工作方式和console application的不同,基本上是這樣運行的,程序從WinMain()開始,然後進入一個message loop,程序在這裏等待發給它的所有消息然後一一處理,直到接收到WM_QUIT的消息的時候,message loop終止,程序結束。所以整個主程序運行的過程就是等待消息,接收消息,然後處理消息的過程。

    窗口建立的時候CreateWindow, RegisterWindow之類的不必太費心,MFC已經全管理妥當了,需要提起一點注意的是程序開始時HINSTANCE hInstance這個參數,在和DLL打交道的時候會幫你解決很多問題,如果一個Bitmap Load不上來,或者一個Dialog DoModal之後不出來,估計就得向這個參數求助了,這是後話。

    具體處理的消息的函數叫window procedure,具體處理消息的code叫message handler。它可以是當前應用程序的API,也可以是調用的不同DLL的API。不同的DLL叫不同的module。以後的文章中我會具體說明module state。是個很重要的話題。(當項目大的時候)

   沒有message handler的消息交給DefWindowProc()函數處理,差不多可以理解爲什麼也不作了。

   消息包括四個參數,window handle,message ID,和另外兩個參數wParam, lParam。window handle可以作爲window的識別ID來用。所以在發送消息的時候如果可以有兩種格式:
CWnd *pWnd = ....
if (pWnd && pWnd->GetSafeWnd())

pWnd->SendMessage(message, wParam = 0, lParam);
或者
SendMessage(pWnd->GetSafeWnd(), message, wParam, lParam )

發送消息如果用SendMessage消息將立刻發送,如果用PostMessage,消息將進入Message queue按當前順序發送,一般沒有特別的要求PostMessage已經足夠了。
處理消息的時候根據不同的Message ID交給不同的message handler去處理,一般的message handler的接收格式是用wParam傳一個關鍵的參數,如這次操作的具體ID,把其餘的大量輔助信息放在lParam裏。需要注意的是如果lParam傳遞的是一個
指針(一般情況下是CObject類的或從CObject衍生出來的),這個指針指向的變量的壽命需要足夠長,因爲信息Post出去之後發送函數很可能就運行完畢了。如果發送的指針是個局部變量,接收方就一定會Crash。當然如果是發送方new出來的變量,接收方得負責幫他delete掉,這個操作很危險,而且不一定合適。有時候發送方把信息傳給N個窗口,第一個窗口delete掉了第二個窗口就麻煩了,不delete掉又不能保證第二個窗口一定delete掉,所以如果可能,不用new爲上策。用點什麼成員變量,常數變量之類的比較好。

由於可能收到的信息種類很多,用傳統的switch來處理在程序中會顯得很亂,於是MFC採用了Message Map機制。Message Map 機制實現了收到的信息和處理信息函數的mapping。在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之間定義的消息會在window接收到之後一一傳給對應的message handler處理。

所有用於處理信息的函數的申明需要有afx_msg關鍵字。對於系統所要處理的message,ON_WN_XXX 一般跟三個參數,WPARAM(wParam),LOWORD(lParam) 和HIWORD(lParam).用不到的參數會省略。

下面列舉一下Message Map中可能用到的關於宏

1。Window間SendMessage或PostMessage收到的消息如果是系統將要管理的,在Message Map中一般用ON_WM_XXX。“XXX”是具體消息名字。例如畫窗口是ON_WM_PAINT。如果是自定義的消息ON_MESSAGE()。例如窗口pWndA發消息給窗口pWndB。pWndB->PostMessage(WM_MYMSG1),那麼Window B要處理這個消息需要在Message Map裏面寫上 ON_MESSAGE(WM_MYMSG1, OnMessage1),然後寫OnMessage1函數作message handler。WM_MYMSG1的定義應放在user message中,WM_USER+NNN。注意最好不要和其它已有的ID重複,這個沒有辦法自動檢查。

2。ON_COMMAND,ON_UPDATE_COMMAND_UI,ON_COMAND_RANGE
ON_COMMAND只要用於menu和toolbar的點擊處理,也可以用在accelerator中。管理用戶的鍵盤輸入用ON_COMMAND比管理ON_WN_CHAR好。ON_UPDATE_COMMAND_UI是用於更新menu或toolbar的。在這個message handler裏面你可以根據不同的要求enable或者disable當前的菜單選項或toolbar button。ON_COMMAND_RANGE主要用於動態的菜單的選項中。當N個動態菜單選項加入時,你可以用一串連續的ID作爲它們的消息管理ID,在ON_COMMAND_RANGE定義這串ID的起始和最大值,然後響應
函數就可以知道具體是哪個動態菜單選項被選中了。

3。ON_COMMAND_EX,ON_COMMAND_RANGE_EX
用處比較少,當多個class (CCmdTarget class)需要處理同一消息的時候,一個class如果用ON_COMMAND_EX處理,返回TRUE,表示消息處理完畢。返回FALSE則其它class可以繼續處理。

4。ON_REGISTERED_MESSAGE
用於確認系統中新message ID唯一。

5。ON_CONTROL,ON_WM_XXX_REFLECT,ON_CONTROL_RANGE
用於window接收其控件發來的特殊消息。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章