duilib庫分析: 消息流程分析


看下CWindowWnd類與CPaintManagerUI類是咋進行消息分發的吧.

 

1. 先看下CPaintManagerUI類的MessageLoop函數:

 

  1. void CPaintManagerUI::MessageLoop()  
  2. {  
  3.     MSG msg = { 0 };  
  4.     while( ::GetMessage(&msg, NULL, 0, 0) ) {    // 獲取消息  
  5.         if( !CPaintManagerUI::TranslateMessage(&msg) ) { // 消息過濾  
  6.             ::TranslateMessage(&msg);  
  7.             ::DispatchMessage(&msg); // 分發到窗口的消息處理窗口中. 也就是調用CWindowWnd類的__WndProc函數或是__ControlProc函數.  
  8.         }  
  9.     }  
  10. }  

消息第一次會由CPaintManagerUI類的TranslateMessage消息接收到.

 

2. 調用CWindowWnd::Create創建窗口.  完成以下操作:

1) 如果要子類下Window的控件(就是系統的控件, 而不是duilib的模擬控件), 就設置__ControlProc函數為消息回調函數.

2)不子類化, 就注冊窗口類. 此時設置__WndProc為窗口消息處理回調函數.  

3)用CreateWindowEx API函數創建窗口.

這里先不看子類化相關的, 我要先看明白標准的窗口創建過程.  這也操作后消息就會分發到__WndProc了, 

3. 看下__WndProc函數的定義: 

 

  1. LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     CWindowWnd* pThis = NULL;  
  4.     if( uMsg == WM_NCCREATE ) { // 要在此消息中把類於窗口進行綁定  
  5.         LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);   // 來自於CreateWindowEx函數的最后一個參數( 也就是CWindowWnd對象指針了 )  
  6.         pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);  
  7.         pThis->m_hWnd = hWnd;  
  8.         ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis)); // 設置到窗口的用戶數據中  
  9.     }   
  10.     else {  
  11.         pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));  
  12.         if( uMsg == WM_NCDESTROY && pThis != NULL ) {  
  13.             LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);    // 收到窗口能處理到的最后一個消息了, 要進行收尾工作了.  
  14.             ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);    // 取消類對象與窗口的綁定關系  
  15.             if( pThis->m_bSubclassed ) pThis->Unsubclass();  
  16.             pThis->m_hWnd = NULL;  
  17.             pThis->OnFinalMessage(hWnd);  
  18.             return lRes;  
  19.         }  
  20.     }  
  21.     if( pThis != NULL ) {  
  22.         return pThis->HandleMessage(uMsg, wParam, lParam);  // 在此調用繼承類的消息處理函數  
  23.     }   
  24.     else {  
  25.         return ::DefWindowProc(hWnd, uMsg, wParam, lParam); // 未綁定類對象, 就調用默認的窗口消息處理函數  
  26.     }  
  27. }  



 

 

 

消息第二次就由__WndProc接收到, 然后再傳到CWindowWnd類的HandlerMessage函數中.

 

 

3. 看看CWindowWnd類的繼承類對於HandlerMessage虛函數的實現.

 

  1. LRESULT CMainWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam )  
  2. {  
  3.     LRESULT lRes        = 0;    // 消息處理返回值.  
  4.     BOOL    bHandled    = TRUE; // 消息是否要繼續往下傳.  
  5.     switch ( uMsg )  
  6.     {  
  7.     case WM_CREATE: lRes    = OnInitResource( bHandled ); break;  // 進行初始化工作. 比如最重要的XML加載解析工作.  
  8.     default:  
  9.         bHandled    = FALSE;  
  10.     }  
  11.   
  12.     if ( bHandled )  
  13.     {  
  14.         return lRes;  
  15.     }  
  16.   
  17.     if ( m_pm.MessageHandler( uMsg, wParam, lParam, lRes ) )    // 傳給CPaintManagerUI::MessageHandler函數進行具體的控件處理工作  
  18.     {  
  19.         return lRes;  
  20.     }  
  21.   
  22.     return CWindowWnd::HandleMessage( uMsg, wParam, lParam );  // 沒處理過的就調用CWindowWnd類的默認消息處理函數吧.  
  23. }  



 

 

 

在這里就是用戶要按消息進行具體的處理了. 之后要傳到CPaintManagerUI類對象的MessageHandler函數. 未處理的消息就要返回給CWindowWnd類的默認消息處理函數來處理了. 

 

4.  CPaintManagerUI類的TranslateMessage, MessageHandler函數的內容.

 

  1. BOOL CPaintManagerUI::TranslateMessage(const LPMSG pMsg)  
  2. {  
  3.     HWND hwndParent = ::GetParent(pMsg->hwnd); // 獲取消息接收窗口的父窗口  
  4.     UINT uStyle = GetWindowStyle(pMsg->hwnd); // 獲取窗口的樣式  
  5.     LRESULT lRes = 0;  
  6.     forint i = 0; i < m_aPreMessages.GetSize(); i++ ) { // 這個m_aPreMessage保存着CPaintManagerUI類對象.   
  7.         CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);  
  8.         if( pMsg->hwnd == pT->GetPaintWindow() // 消息是否屬於當前CPaintManagerUI綁定的窗口  
  9.          || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) ) // 消息是否為當前窗口中窗口的消息, (如ActiveX控件 )  
  10.         {  
  11.             if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return TRUE; // 此時就調用PreMessageHandler過濾函數.  
  12.         }  
  13.     }  
  14.     return FALSE;  
  15. }  

m_aPreMessage為靜態成員變量, 在CPaintManagerUI::Init進行窗口與此類綁定時添加到此變量中. 

 

 

5. CPaintManagerUI::PreMessageHandler消息過濾函數.

 

  1. BOOL CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT/*lRes*/)  
  2. {  
  3.     // 遍歷當前的消息過濾列表. m_aPreMessageFilter的元素為IMessageFilterUI接口.只一個虛函數MessageHandler.   
  4.     // 用戶可以添加此接口的繼承類變量到m_aPreMessageFilters列表中. ( 調用AddMessageFilter函數實現 )  
  5.     forint i = 0; i < m_aPreMessageFilters.GetSize(); i++ )   
  6.     {  
  7.         BOOL bHandled = FALSE;  
  8.         LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);  
  9.         if( bHandled ) {  
  10.             return TRUE;  
  11.         }  
  12.     }  
  13.     // 以下是對幾個按鍵消息的過濾.  
  14.     // WM_KEYDOWN     檢查是否為VK_TAB鍵, 要進行控件焦點的移動.  
  15.     // WM_SYSCHAR     獲取與wParam中的字符加速鍵匹配的控件, 並激活它.  
  16.     // WM_SYSKEYDOWN  生成控件事件( 用TEventUI來模擬 )  
  17. }  



 

 

 

 

 

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函數來投遞的.

 

 


免責聲明!

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



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