MFC的消息機制
MFC的消息循環(::GetMessage,::PeekMessage)消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之間的路由是兩件不同的事情 分兩個步驟完成: 1 “消息派送”:非對話框程序(MFC Doc/View架構)和對話框程序略有不同,但原理相差不大。但除了少數的消息例如WM_QUIT,所有消息的派送流程基本一致。2 “消息路由”:其中消息分有3種類型,各類型的路由過程不一樣。非對話框程序和對話框程序的處理相同。 消息派送消息派送過程如下圖
注意:方框代表類函數,圓圈代表執行函數的對象實體。類函數和執行函數的對象一定要分清楚。 程序啟動后,構造全局變量tApp,并調用_tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run最終進入tApp對象的 CWinThread::Run 函數中。其中右框圖中所有操作都是tApp對象完成的。
CWinThread的Run函數里面,調用了PumpMessage函數。PumpMessage是關鍵函數,它做了兩件事情:調用PreTranslateMessage。MFC的消息預處理機制。 調用DispatchMessage做消息分發。 其中PreTranslateMessage函數的作用是:讓每個消息的目標窗口(包括他的父窗口,注意不是父類)都有機會參與消息到來之前的處理。下文有具體該函數的內部處理。 DispatchMessage則調用AfxWndProc ,AfxWndProc 調用AfxCallWndProc ,而AfxCallWndProc 則做了關鍵的一步驟,調用 pWnd->WindowProc(nMsg, wParam, lParam);其中 pWnd為目標窗口對象。完成消息派送,消息成功派送到目標窗口對象的WindowProc函數中。 而后的事情,就是“目標窗口對象”收到消息后,進行消息路由和映射,找到改消息的處理函數。 再看看PreTranslate的內部處理:
tApp對象的PreTranslateMessage操作有3種走向:如果(pMsg->hwnd == NULL),說明這是一個線程消息。調用CWinThread::DispatchThreadMessageEx到消息映射表找到消息入口,然后調用消息處理函數。
NOTE: 一般用PostThreadMessage函數發送線程之間的消息,他和窗口消息不同,需要指定線程id,消息被系統放入到目標線程的消息隊列中;用ON_THREAD_MESSAGE( message, memberFxn )宏可以映射線程消息和他的處理函數。這個宏必須在應用程序類(從CWinThread繼承)中,因為只有應用程序類才處理線程消息。
消息的目標窗口的PreTranslateMessage函數首先得到消息處理權,如果函數返回FALSE,那么他的父窗口將得到消息的處理權,直到主窗口;如果函數返回TRUE(表示消息已經被處理了),那么就不需要調用父類的PreTranslateMessage函數。這樣,保證了消息的目標窗口以及他的父窗口都可以有機會調用PreTranslateMessage--在消息發送到窗口之前進行預處理 如果消息的目標窗口和主窗口沒有父子關系,那么再調用主窗口的PreTranslateMessage函數。例如非模式對話框
消息路由/映射
到現在消息已經被目標窗口得到了。
主要分2種情況去找處理函數:WM_COMMAND,WM_NOTIFY處理類似
NOTE:MFC 把消息分為三大類:
命令消息(WM_COMMAND):凡由UI 對象產生的消息都是這種命令消息,可能來自菜單或加速鍵或工具欄。SDK程序主要靠消息的wParam 辨識之,MFC 程序則主要靠菜單項目的識別碼(menu ID)辨識之-- 兩者其實是相同的。
凡衍生自CCmdTarget 的類別,皆有資格接收改類型消息。幾乎構造應用程序的最重要的幾個類別都衍生自CCmdTarget。標準消息- 除WM_COMMAND 之外,任何以WM_ 開頭的都算是這一類。任何
衍生自CWnd 之類別,均可接收此消息。Control Notification - 這種消息由控制組件產生,為的是向其父窗口通知某種情況。例如當你在ListBox 上選擇其中一個項目,ListBox 就
會產生LBN_SELCHANGE 傳送給父窗口。這類消息也是以WM_COMMAND 形
式呈現。
“標準消息”處理最直觀,直接調用AfxFindMessageEntry函數去“消息映射表”中尋找對應的處理函數。遍歷順序是沿著父類一直上溯。其中消息映射表是MFC代碼類似于DECLARE_MESSAGE_MAP()等的宏搭建起來的網。
按照MFC的規定。WM_COMMAND消息就不同了,消息經過了一個由MFC指定的路線。
路線圖如下所示:
就是在OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)實現了這個路線。
至此。消息處理全部完成。全圖如下:
其他備注:
只要線程有界面元素或者調用GetMessage,或者有線程消息發送過來,系統就會為線程創建一個消息隊列。窗口屬于創建他的線程。用SendMessage等發送消息到指定窗口,則把該消息放到窗口所在的消息隊列。或者可以直接用PostThreadMessage給指定id線程發送消息。 ::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)的最后一個參數指定檢查消息后,把不把消息移出消息隊列。 關注OnIdle函數:在CThreadWnd發現消息隊列中并沒有消息的時候,則調用該函數。用戶可重載該函數。在這個處理中將更新UI界面(比如工具欄按鈕的enable和disable狀態),刪除臨時對象(比如用FromHandle得到的對象指針。由于這個原因,在函數之間傳遞由FromHandle得到的對象指針是不安全的,因為他沒有持久性)
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成