Duilib消息路由


本文若有不對之處,敬請指出。
我們知道win32窗口程序的基本流程:

註冊窗口類 -> 創建窗口 —> 顯示窗口 -> 消息循環 -> 消息流轉

基於這個路線,我們來探索下duilib庫的實現。
毋庸置疑,先看一例測試程序中的WinMain函數:



CFrameWindowWnd* pFrame = new CFrameWindowWnd(); 
 if( pFrame == NULL ) return 0; 
 pFrame->Create(NULL, _T("測試"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE); 
 pFrame->ShowWindow(true); 
 CPaintManagerUI::MessageLoop(); 


可以確定註冊窗口類和創建窗口都在CFrameWindowWnd::Create中進行。其中CFrameWindowWnd的一個基類是CWindowWnd,進入到該類Create方法中:



HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
ASSERT(m_hWnd!=NULL);
return m_hWnd;
}




其中GetSuperClassName爲虛函數,用於指定要子類化的窗口類。關於窗口子類化的處理此處不予關注。

如果不需要子類化,則調用RegisterWindowClass進行窗口類註冊。進入該方法內,可以發現註冊窗的口過程是CWindowWnd::__WndProc;

註冊之後,調用API CreateWindowEx創建窗口。

創建窗口後,就該最關鍵的消息循環和消息路由了,here we go!

首先看看消息循環的處理CPaintManagerUI::MessageLoop:





[cpp] view plain copy
01.void CPaintManagerUI::MessageLoop()
02.{
03. MSG msg = { 0 };
04. while( ::GetMessage(&msg, NULL, 0, 0) ) {
05. if( !CPaintManagerUI::TranslateMessage(&msg) ) {
06. ::TranslateMessage(&msg);
07. ::DispatchMessage(&msg);
08. }
09. }
10.}


可以看出,任何消息都會經過CPaintManagerUI::TranslateMessage的過濾,如果該方法沒有處理消息,纔將消息路由給窗口過程。相當於MFC中的PreTranslateMessage。

然後我們看下CPaintManagerUI::TranslateMessage的實現:




[cpp] view plain copy
01.bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg)
02.{
03. // Pretranslate Message takes care of system-wide messages, such as
04. // tabbing and shortcut key-combos. We'll look for all messages for
05. // each window and any child control attached.
06. HWND hwndParent = ::GetParent(pMsg->hwnd);
07. UINT uStyle = GetWindowStyle(pMsg->hwnd);
08. LRESULT lRes = 0;
09. for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) {
10. CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);
11. if( pMsg->hwnd == pT->GetPaintWindow()
12. || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) )
13. {
14. if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true;
15. }
16. }
17. return false;
18.}






m_aPreMessages 的定義爲:static CStdPtrArray m_aPreMessages; 當創建一個主窗口時,m_aPreMessages一般會有插入動作,因此,此數組是所有相關窗口attach的CPaintManagerUI對象地址的集合,處理的是整個程序的全局的消息。當msg消息路由至此處時,如果該窗口是子窗口或者消息窗口本身,就調用PreMessageHandler處理,如果仍未得到處理,則返回false,傳遞給窗口註冊的窗口消息處理。

接下來看看CPaintManagerUI::PreMessageHandler的實現:




[cpp] view plain copy
01.bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)
02.{
03. for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ )
04. {
05. bool bHandled = false;
06. LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);
07. if( bHandled ) {
08. return true;
09. }
10. }
11. switch( uMsg ) {
12. case WM_KEYDOWN:
13. {
14. // Tabbing between controls
15. if( wParam == VK_TAB ) {
16. if( m_pFocus && m_pFocus->IsVisible() && m_pFocus->IsEnabled() && _tcsstr(m_pFocus->GetClass(), _T("RichEditUI")) != NULL ) {
17. if( static_cast<CRichEditUI*>(m_pFocus)->IsWantTab() ) return false;
18. }
19. SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0);
20. return true;
21. }
22. }
23. break;
24. case WM_SYSCHAR:
25. {
26. // Handle ALT-shortcut key-combinations
27. FINDSHORTCUT fs = { 0 };
28. fs.ch = toupper((int)wParam);
29. CControlUI* pControl = m_pRoot->FindControl(__FindControlFromShortcut, &fs, UIFIND_ENABLED | UIFIND_ME_FIRST | UIFIND_TOP_FIRST);
30. if( pControl != NULL ) {
31. pControl->SetFocus();
32. pControl->Activate();
33. return true;
34. }
35. }
36. break;
37. case WM_SYSKEYDOWN:
38. {
39. if( m_pFocus != NULL ) {
40. TEventUI event = { 0 };
41. event.Type = UIEVENT_SYSKEY;
42. event.chKey = (TCHAR)wParam;
43. event.ptMouse = m_ptLastMousePos;
44. event.wKeyState = MapKeyState();
45. event.dwTimestamp = ::GetTickCount();
46. m_pFocus->Event(event);
47. }
48. }
49. break;
50. }
51. return false;
52.}




這裏又新增了一個對象屬性:m_aPreMessageFilters,是一個窗口類(派生CWindowWnd)對象的集合。同時該對象的類必須是IMessageFilterUI和CWindowWnd的子類。通過這個集合,app將消息推送到每一個需要預處理所關心的消息的窗口,即調用MessageHandler,此方法是我們要重載的重要方法之一,用於對消息傳遞給窗口過程之前的預處理。接下來的是duilib提供的預處理,switch塊處理的是全局級消息,對其進行預處理,包括tab處理,alt組合鍵和系統按鍵消息。





必須要注意,CPaintManagerUI::MessageHandler爲我們提供了多數消息的默認實現。


至此,消息的預處理階段完成,回到CPaintManagerUI::MessageLoop,未處理的消息將傳遞給窗口過程:





[cpp] view plain copy
01.LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
02.{
03. CWindowWnd* pThis = NULL;
04. if( uMsg == WM_NCCREATE ) {
05. LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
06. pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
07. pThis->m_hWnd = hWnd;
08. ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
09. }
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.}





此過程很簡單,對窗口創建過程中的第一個消息WM_NCCREATE進行處理,對其窗口attach窗口對象地址。HandleMessage是一個虛函數,需要我們進行重載,此接口很重要,相當於MFC中的具體的一個窗口類的消息映射表,win32中的具體的窗口過程。

下面的代碼展示了HandleMessage的典型實現:




[cpp] view plain copy
01.LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
02. {
03. if( uMsg == WM_CREATE ) {
04. m_pm.Init(m_hWnd);
05. CControlUI *pButton = new CButtonUI;
06. pButton->SetName(_T("closebtn"));
07. pButton->SetBkColor(0xFFFF0000);
08. m_pm.AttachDialog(pButton);
09. m_pm.AddNotifier(this);
10. return 0;
11. }
12. else if( uMsg == WM_DESTROY ) {
13. ::PostQuitMessage(0);
14. }
15. else if( uMsg == WM_NCACTIVATE ) {
16. if( !::IsIconic(m_hWnd) ) {
17. return (wParam == 0) ? TRUE : FALSE;
18. }
19. }
20. else if( uMsg == WM_NCCALCSIZE ) {
21. return 0;
22. }
23. else if( uMsg == WM_NCPAINT ) {
24. return 0;
25. }
26.
27. LRESULT lRes = 0;
28. if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
29. return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
30. }







至此,消息路由大體就說完了,有興趣的可以畫個圖,一目瞭然。
本文若有不對之處,敬請指出。


我們知道win32窗口程序的基本流程:

註冊窗口類 -> 創建窗口  —> 顯示窗口 -> 消息循環  -> 消息流轉

基於這個路線,我們來探索下duilib庫的實現。


毋庸置疑,先看一例測試程序中的WinMain函數:

  1. CFrameWindowWnd* pFrame = new CFrameWindowWnd();  
  2.     if( pFrame == NULL ) return 0;  
  3.     pFrame->Create(NULL, _T("測試"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);  
  4.     pFrame->ShowWindow(true);  
  5.     CPaintManagerUI::MessageLoop();  
CFrameWindowWnd* pFrame = new CFrameWindowWnd();
	if( pFrame == NULL ) return 0;
	pFrame->Create(NULL, _T("測試"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
	pFrame->ShowWindow(true);
	CPaintManagerUI::MessageLoop();


可以確定註冊窗口類和創建窗口都在CFrameWindowWnd::Create中進行。其中CFrameWindowWnd的一個基類是CWindowWnd,進入到該類Create方法中:

  1. HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)  
  2. {  
  3.     if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;  
  4.     if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;  
  5.     m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);  
  6.     ASSERT(m_hWnd!=NULL);  
  7.     return m_hWnd;  
  8. }  
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
    if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
    if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
    m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
    ASSERT(m_hWnd!=NULL);
    return m_hWnd;
}

其中GetSuperClassName爲虛函數,用於指定要子類化的窗口類。關於窗口子類化的處理此處不予關注。

如果不需要子類化,則調用RegisterWindowClass進行窗口類註冊。進入該方法內,可以發現註冊窗的口過程是CWindowWnd::__WndProc;

註冊之後,調用API CreateWindowEx創建窗口。

創建窗口後,就該最關鍵的消息循環和消息路由了,here we go!

首先看看消息循環的處理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);  
  8.         }  
  9.     }  
  10. }  
void CPaintManagerUI::MessageLoop()
{
    MSG msg = { 0 };
    while( ::GetMessage(&msg, NULL, 0, 0) ) {
        if( !CPaintManagerUI::TranslateMessage(&msg) ) {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    }
}

可以看出,任何消息都會經過CPaintManagerUI::TranslateMessage的過濾,如果該方法沒有處理消息,纔將消息路由給窗口過程。相當於MFC中的PreTranslateMessage。

然後我們看下CPaintManagerUI::TranslateMessage的實現:

  1. bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg)  
  2. {  
  3.     // Pretranslate Message takes care of system-wide messages, such as  
  4.     // tabbing and shortcut key-combos. We'll look for all messages for  
  5.     // each window and any child control attached.  
  6.     HWND hwndParent = ::GetParent(pMsg->hwnd);  
  7.     UINT uStyle = GetWindowStyle(pMsg->hwnd);  
  8.     LRESULT lRes = 0;  
  9.     forint i = 0; i < m_aPreMessages.GetSize(); i++ ) {  
  10.         CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);  
  11.         if( pMsg->hwnd == pT->GetPaintWindow()  
  12.             || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) )  
  13.         {  
  14.             if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true;  
  15.         }  
  16.     }  
  17.     return false;  
  18. }  
bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg)
{
    // Pretranslate Message takes care of system-wide messages, such as
    // tabbing and shortcut key-combos. We'll look for all messages for
    // each window and any child control attached.
    HWND hwndParent = ::GetParent(pMsg->hwnd);
    UINT uStyle = GetWindowStyle(pMsg->hwnd);
    LRESULT lRes = 0;
    for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) {
        CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);
        if( pMsg->hwnd == pT->GetPaintWindow()
            || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) )
        {
            if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true;
        }
    }
    return false;
}


m_aPreMessages 的定義爲:static CStdPtrArray m_aPreMessages; 當創建一個主窗口時,m_aPreMessages一般會有插入動作,因此,此數組是所有相關窗口attach的CPaintManagerUI對象地址的集合,處理的是整個程序的全局的消息。當msg消息路由至此處時,如果該窗口是子窗口或者消息窗口本身,就調用PreMessageHandler處理,如果仍未得到處理,則返回false,傳遞給窗口註冊的窗口消息處理。

接下來看看CPaintManagerUI::PreMessageHandler的實現:

  1. bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT/*lRes*/)  
  2. {  
  3.     forint i = 0; i < m_aPreMessageFilters.GetSize(); i++ )   
  4.     {  
  5.         bool bHandled = false;  
  6.         LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);  
  7.         if( bHandled ) {  
  8.             return true;  
  9.         }  
  10.     }  
  11.     switch( uMsg ) {  
  12.     case WM_KEYDOWN:  
  13.         {  
  14.            // Tabbing between controls  
  15.            if( wParam == VK_TAB ) {  
  16.                if( m_pFocus && m_pFocus->IsVisible() && m_pFocus->IsEnabled() && _tcsstr(m_pFocus->GetClass(), _T("RichEditUI")) != NULL ) {  
  17.                    ifstatic_cast<CRichEditUI*>(m_pFocus)->IsWantTab() ) return false;  
  18.                }  
  19.                SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0);  
  20.                return true;  
  21.            }  
  22.         }  
  23.         break;  
  24.     case WM_SYSCHAR:  
  25.         {  
  26.            // Handle ALT-shortcut key-combinations  
  27.            FINDSHORTCUT fs = { 0 };  
  28.            fs.ch = toupper((int)wParam);  
  29.            CControlUI* pControl = m_pRoot->FindControl(__FindControlFromShortcut, &fs, UIFIND_ENABLED | UIFIND_ME_FIRST | UIFIND_TOP_FIRST);  
  30.            if( pControl != NULL ) {  
  31.                pControl->SetFocus();  
  32.                pControl->Activate();  
  33.                return true;  
  34.            }  
  35.         }  
  36.         break;  
  37.     case WM_SYSKEYDOWN:  
  38.         {  
  39.            if( m_pFocus != NULL ) {  
  40.                TEventUI event = { 0 };  
  41.                event.Type = UIEVENT_SYSKEY;  
  42.                event.chKey = (TCHAR)wParam;  
  43.                event.ptMouse = m_ptLastMousePos;  
  44.                event.wKeyState = MapKeyState();  
  45.                event.dwTimestamp = ::GetTickCount();  
  46.                m_pFocus->Event(event);  
  47.            }  
  48.         }  
  49.         break;  
  50.     }  
  51.     return false;  
  52. }  
bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)
{
    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;
        }
    }
    switch( uMsg ) {
    case WM_KEYDOWN:
        {
           // Tabbing between controls
           if( wParam == VK_TAB ) {
               if( m_pFocus && m_pFocus->IsVisible() && m_pFocus->IsEnabled() && _tcsstr(m_pFocus->GetClass(), _T("RichEditUI")) != NULL ) {
                   if( static_cast<CRichEditUI*>(m_pFocus)->IsWantTab() ) return false;
               }
               SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0);
               return true;
           }
        }
        break;
    case WM_SYSCHAR:
        {
           // Handle ALT-shortcut key-combinations
           FINDSHORTCUT fs = { 0 };
           fs.ch = toupper((int)wParam);
           CControlUI* pControl = m_pRoot->FindControl(__FindControlFromShortcut, &fs, UIFIND_ENABLED | UIFIND_ME_FIRST | UIFIND_TOP_FIRST);
           if( pControl != NULL ) {
               pControl->SetFocus();
               pControl->Activate();
               return true;
           }
        }
        break;
    case WM_SYSKEYDOWN:
        {
           if( m_pFocus != NULL ) {
               TEventUI event = { 0 };
               event.Type = UIEVENT_SYSKEY;
               event.chKey = (TCHAR)wParam;
               event.ptMouse = m_ptLastMousePos;
               event.wKeyState = MapKeyState();
               event.dwTimestamp = ::GetTickCount();
               m_pFocus->Event(event);
           }
        }
        break;
    }
    return false;
}

這裏又新增了一個對象屬性:m_aPreMessageFilters,是一個窗口類(派生CWindowWnd)對象的集合。同時該對象的類必須是IMessageFilterUI和CWindowWnd的子類。通過這個集合,app將消息推送到每一個需要預處理所關心的消息的窗口,即調用MessageHandler,此方法是我們要重載的重要方法之一,用於對消息傳遞給窗口過程之前的預處理。接下來的是duilib提供的預處理,switch塊處理的是全局級消息,對其進行預處理,包括tab處理,alt組合鍵和系統按鍵消息。


必須要注意,CPaintManagerUI::MessageHandler爲我們提供了多數消息的默認實現。


至此,消息的預處理階段完成,回到CPaintManagerUI::MessageLoop,未處理的消息將傳遞給窗口過程:

  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);  
  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. }  
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);
        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);
    }
}


此過程很簡單,對窗口創建過程中的第一個消息WM_NCCREATE進行處理,對其窗口attach窗口對象地址。HandleMessage是一個虛函數,需要我們進行重載,此接口很重要,相當於MFC中的具體的一個窗口類的消息映射表,win32中的具體的窗口過程。

下面的代碼展示了HandleMessage的典型實現:

  1. LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2.     {  
  3.         if( uMsg == WM_CREATE ) {  
  4.             m_pm.Init(m_hWnd);  
  5.             CControlUI *pButton = new CButtonUI;  
  6.             pButton->SetName(_T("closebtn"));  
  7.             pButton->SetBkColor(0xFFFF0000);  
  8.             m_pm.AttachDialog(pButton);  
  9.             m_pm.AddNotifier(this);  
  10.             return 0;  
  11.         }  
  12.         else if( uMsg == WM_DESTROY ) {  
  13.             ::PostQuitMessage(0);  
  14.         }  
  15.         else if( uMsg == WM_NCACTIVATE ) {  
  16.             if( !::IsIconic(m_hWnd) ) {  
  17.                 return (wParam == 0) ? TRUE : FALSE;  
  18.             }  
  19.         }  
  20.         else if( uMsg == WM_NCCALCSIZE ) {  
  21.             return 0;  
  22.         }  
  23.         else if( uMsg == WM_NCPAINT ) {  
  24.             return 0;  
  25.         }  
  26.   
  27.         LRESULT lRes = 0;  
  28.         if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;  
  29.         return CWindowWnd::HandleMessage(uMsg, wParam, lParam);  
  30.     }  
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		if( uMsg == WM_CREATE ) {
			m_pm.Init(m_hWnd);
			CControlUI *pButton = new CButtonUI;
			pButton->SetName(_T("closebtn"));
			pButton->SetBkColor(0xFFFF0000);
			m_pm.AttachDialog(pButton);
			m_pm.AddNotifier(this);
			return 0;
		}
		else if( uMsg == WM_DESTROY ) {
			::PostQuitMessage(0);
		}
		else if( uMsg == WM_NCACTIVATE ) {
			if( !::IsIconic(m_hWnd) ) {
				return (wParam == 0) ? TRUE : FALSE;
			}
		}
		else if( uMsg == WM_NCCALCSIZE ) {
			return 0;
		}
		else if( uMsg == WM_NCPAINT ) {
			return 0;
		}

		LRESULT lRes = 0;
		if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
		return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
	}


至此,消息路由大體就說完了,有興趣的可以畫個圖,一目瞭然。


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