轉
看下CWindowWnd類與CPaintManagerUI類是咋進行消息分發的吧.
1. 先看下CPaintManagerUI類的MessageLoop函數:
- void CPaintManagerUI::MessageLoop()
- {
- MSG msg = { 0 };
- while( ::GetMessage(&msg, NULL, 0, 0) ) { // 獲取消息
- if( !CPaintManagerUI::TranslateMessage(&msg) ) { // 消息過濾
- ::TranslateMessage(&msg);
- ::DispatchMessage(&msg); // 分發到窗口的消息處理窗口中. 也就是調用CWindowWnd類的__WndProc函數或是__ControlProc函數.
- }
- }
- }
消息第一次會由CPaintManagerUI類的TranslateMessage消息接收到.
2. 調用CWindowWnd::Create創建窗口. 完成以下操作:
1) 如果要子類下Window的控件(就是系統的控件, 而不是duilib的模擬控件), 就設置__ControlProc函數為消息回調函數.
2)不子類化, 就注冊窗口類. 此時設置__WndProc為窗口消息處理回調函數.
3)用CreateWindowEx API函數創建窗口.
這里先不看子類化相關的, 我要先看明白標准的窗口創建過程. 這也操作后消息就會分發到__WndProc了,
3. 看下__WndProc函數的定義:
- LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- CWindowWnd* pThis = NULL;
- if( uMsg == WM_NCCREATE ) { // 要在此消息中把類於窗口進行綁定
- LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); // 來自於CreateWindowEx函數的最后一個參數( 也就是CWindowWnd對象指針了 )
- pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
- pThis->m_hWnd = hWnd;
- ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis)); // 設置到窗口的用戶數據中
- }
- else {
- pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
- if( uMsg == WM_NCDESTROY && pThis != NULL ) {
- LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam); // 收到窗口能處理到的最后一個消息了, 要進行收尾工作了.
- ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L); // 取消類對象與窗口的綁定關系
- if( pThis->m_bSubclassed ) pThis->Unsubclass();
- pThis->m_hWnd = NULL;
- pThis->OnFinalMessage(hWnd);
- return lRes;
- }
- }
- if( pThis != NULL ) {
- return pThis->HandleMessage(uMsg, wParam, lParam); // 在此調用繼承類的消息處理函數
- }
- else {
- return ::DefWindowProc(hWnd, uMsg, wParam, lParam); // 未綁定類對象, 就調用默認的窗口消息處理函數
- }
- }
消息第二次就由__WndProc接收到, 然后再傳到CWindowWnd類的HandlerMessage函數中.
3. 看看CWindowWnd類的繼承類對於HandlerMessage虛函數的實現.
- LRESULT CMainWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam )
- {
- LRESULT lRes = 0; // 消息處理返回值.
- BOOL bHandled = TRUE; // 消息是否要繼續往下傳.
- switch ( uMsg )
- {
- case WM_CREATE: lRes = OnInitResource( bHandled ); break; // 進行初始化工作. 比如最重要的XML加載解析工作.
- default:
- bHandled = FALSE;
- }
- if ( bHandled )
- {
- return lRes;
- }
- if ( m_pm.MessageHandler( uMsg, wParam, lParam, lRes ) ) // 傳給CPaintManagerUI::MessageHandler函數進行具體的控件處理工作
- {
- return lRes;
- }
- return CWindowWnd::HandleMessage( uMsg, wParam, lParam ); // 沒處理過的就調用CWindowWnd類的默認消息處理函數吧.
- }
在這里就是用戶要按消息進行具體的處理了. 之后要傳到CPaintManagerUI類對象的MessageHandler函數. 未處理的消息就要返回給CWindowWnd類的默認消息處理函數來處理了.
4. CPaintManagerUI類的TranslateMessage, MessageHandler函數的內容.
- BOOL CPaintManagerUI::TranslateMessage(const LPMSG pMsg)
- {
- HWND hwndParent = ::GetParent(pMsg->hwnd); // 獲取消息接收窗口的父窗口
- UINT uStyle = GetWindowStyle(pMsg->hwnd); // 獲取窗口的樣式
- LRESULT lRes = 0;
- for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { // 這個m_aPreMessage保存着CPaintManagerUI類對象.
- CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);
- if( pMsg->hwnd == pT->GetPaintWindow() // 消息是否屬於當前CPaintManagerUI綁定的窗口
- || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) ) // 消息是否為當前窗口中窗口的消息, (如ActiveX控件 )
- {
- if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return TRUE; // 此時就調用PreMessageHandler過濾函數.
- }
- }
- return FALSE;
- }
m_aPreMessage為靜態成員變量, 在CPaintManagerUI::Init進行窗口與此類綁定時添加到此變量中.
5. CPaintManagerUI::PreMessageHandler消息過濾函數.
- BOOL CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)
- {
- // 遍歷當前的消息過濾列表. m_aPreMessageFilter的元素為IMessageFilterUI接口.只一個虛函數MessageHandler.
- // 用戶可以添加此接口的繼承類變量到m_aPreMessageFilters列表中. ( 調用AddMessageFilter函數實現 )
- for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ )
- {
- BOOL bHandled = FALSE;
- LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);
- if( bHandled ) {
- return TRUE;
- }
- }
- // 以下是對幾個按鍵消息的過濾.
- // WM_KEYDOWN 檢查是否為VK_TAB鍵, 要進行控件焦點的移動.
- // WM_SYSCHAR 獲取與wParam中的字符加速鍵匹配的控件, 並激活它.
- // WM_SYSKEYDOWN 生成控件事件( 用TEventUI來模擬 )
- }
5. CPaintManagerUI::MessageHandler函數.
1) 遍歷m_aMessageFilters列表中的IMessageFilterUI接口, 並調用MessageHandler函數, 再次進行相關的消息過濾功能.(與上面的m_aPreMessageFilters類似)
2) 在此會處理窗口的WM_PAINT消息. 顯示所有控件的外觀與狀態.
3) 處理鼠標事件, 實現控件激活和相關事件.
4) 處理WM_TIMER消息, 所有控件要用CPaintManagerUI的SetTimer, KillTimer等函數實現計時器功能.
5) 處理CPaintManagerUI類的自定消息, WM_APP + 1與 +2,
WM_APP + 1是用於控件延遲銷毀控件對象
WM_APP + 2銷毀異步消息的處理.
( 異步控件消息用CPaintManagerUI::SendNotify函數, 把消息對象添加到m_aAsyncNotify列表中, 再PostMessage函數WM_APP + 2 )
5) 其它基本的窗口相關消息的處理.
CPaintManagerUI把DUILIB內部的事件都是用TEventUI結構的形式調用CControlUI類的Event函數來投遞的.
