WTL 學習筆記 -- 消息流

 

正如剛從DOS轉到Win32下寫程序時,總是爲找不到main函數而感到不爽,學習時WTL時,第一反應是找GetMessageDispatchMessage,想知道消息是如何分發到窗口的。

 

_tWinMain裏做了一些初始化函數之後,就進入了Run函數,顯然Run函數就是消息循環。下面是Run函數的代碼:

 

int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)

{

         CMessageLoop theLoop;

         _Module.AddMessageLoop(&theLoop);

 

         CMainFrame wndMain;

 

         if(wndMain.CreateEx() == NULL)

         {

                   ATLTRACE(_T("Main window creation failed!/n"));

                   return 0;

         }

 

         wndMain.ShowWindow(nCmdShow);

 

         int nRet = theLoop.Run();

 

         _Module.RemoveMessageLoop();

         return nRet;

}

 

不出所料,CmessageLoop就是用作消息循環的類,_Module.AddMessageLoop不過是保存一個全局引用,其它地方可以方便的取得CmessageLoop的實例theLoop

 

CmessageLoop::Run函數無疑就是消息循環函數了:

int Run()

         {

                   BOOL bDoIdle = TRUE;

                   int nIdleCount = 0;

                   BOOL bRet;

 

                   for(;;)

                   {

                            while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))

                            {

                                     if(!OnIdle(nIdleCount++))

                                               bDoIdle = FALSE;

                            }

 

                            bRet = ::GetMessage(&m_msg, NULL, 0, 0);

 

                            if(bRet == -1)

                            {

                                     ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)/n"));

                                     continue;   // error, don't process

                            }

                            else if(!bRet)

                            {

                                     ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting/n"));

                                     break;   // WM_QUIT, exit message loop

                            }

 

                            if(!PreTranslateMessage(&m_msg))

                            {

                                     ::TranslateMessage(&m_msg);

                                     ::DispatchMessage(&m_msg);

                            }

 

                            if(IsIdleMessage(&m_msg))

                            {

                                     bDoIdle = TRUE;

                                     nIdleCount = 0;

                            }

                   }

 

                   return (int)m_msg.wParam;

         }

 

CmessageLoop::Run函數,不斷的從消息隊列裏取消息,然後分發給對應的窗口。當消息分發到窗口時,自然就是調用窗口的WINPROC函數了。那麼WINPROC是在哪裏註冊,在哪裏實現的呢?

 

         HWND CwindowImpl::Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,

                            DWORD dwStyle = 0, DWORD dwExStyle = 0,

                            UINT nID = 0, LPVOID lpCreateParam = NULL)

         {

                   if (T::GetWndClassInfo().m_lpszOrigName == NULL)

                            T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();

                   ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);

 

                   dwStyle = T::GetWndStyle(dwStyle);

                   dwExStyle = T::GetWndExStyle(dwExStyle);

 

                   return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rcPos, szWindowName,

                            dwStyle, dwExStyle, nID, atom, lpCreateParam);

         }

 

 

原來在窗口創建函數裏註冊WinClass,因爲這是在基類裏實現的,它需要從子類窗口中獲得WinClass信息。很明顯,這裏調用GetWndClassInfo獲得WinClass信息。但是我們並沒有看到子類裏實現該函數,應該是宏展開的吧,DECLARE_XXXX_WND_CLASS之類最爲可疑:

#define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) /

static CFrameWndClassInfo& GetWndClassInfo() /

{ /

         static CFrameWndClassInfo wc = /

         { /

                   { 0, StartWindowProc, /

                     0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName }, /

                   NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID /

         }; /

         return wc; /

}

 

 

沒錯,就是它,原來WINPROC函數名爲StartWindowProc。問題又來了:StartWindowProc在哪裏實現了,子類沒有,自然是基類裏了:

 

template <class TBase, class TWinTraits>

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

         CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_Module.ExtractCreateWndData();

         ATLASSERT(pThis != NULL);

         pThis->m_hWnd = hWnd;

         pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);

         WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);

         WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);

#ifdef _DEBUG

         // check if somebody has subclassed us already since we discard it

         if(pOldProc != StartWindowProc)

                   ATLTRACE2(atlTraceWindowing, 0, _T("Subclassing through a hook discarded./n"));

#else

         pOldProc; // avoid unused warning

#endif

         return pProc(hWnd, uMsg, wParam, lParam);

}

 

這個函數的實現有點晦澀,主要是不太明白thunk指的是什麼。基本功能倒是很明顯,只是重新設置了一下WINPROC,也就是說WINPROC被換成新的了,下次再也不會調用StartWindowProc了。很快找到了真正的WINPROC

template <class TBase, class TWinTraits>

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

         CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd;

         // set a ptr to this message and save the old value

         MSG msg = { pThis->m_hWnd, uMsg, wParam, lParam, 0, { 0, 0 } };

         const MSG* pOldMsg = pThis->m_pCurrentMsg;

         pThis->m_pCurrentMsg = &msg;

         // pass to the message map to process

         LRESULT lRes;

         BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);

         // restore saved value for the current message

         ATLASSERT(pThis->m_pCurrentMsg == &msg);

         pThis->m_pCurrentMsg = pOldMsg;

         // do the default processing if message was not handled

         if(!bRet)

         {

                  if(uMsg != WM_NCDESTROY)

                            lRes = pThis->DefWindowProc(uMsg, wParam, lParam);

                   else

                   {

                            // unsubclass, if needed

                            LONG pfnWndProc = ::GetWindowLong(pThis->m_hWnd, GWL_WNDPROC);

                            lRes = pThis->DefWindowProc(uMsg, wParam, lParam);

                            if(pThis->m_pfnSuperWindowProc != ::DefWindowProc && ::GetWindowLong(pThis->m_hWnd, GWL_WNDPROC) == pfnWndProc)

                                     ::SetWindowLong(pThis->m_hWnd, GWL_WNDPROC, (LONG)pThis->m_pfnSuperWindowProc);

                            // clear out window handle

                            HWND hWnd = pThis->m_hWnd;

                            pThis->m_hWnd = NULL;

                            // clean up after window is destroyed

                            pThis->OnFinalMessage(hWnd);

                   }

         }

         return lRes;

}

 

在這個函數裏,調用ProcessWindowMessage去分發消息,ProcessWindowMessage是在哪裏實現的呢?沒有找到自然是宏展開的:

#define BEGIN_MSG_MAP(theClass) /

public: /

         BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) /

         { /

                   BOOL bHandled = TRUE; /

                   hWnd; /

                   uMsg; /

                   wParam; /

                   lParam; /

                   lResult; /

                   bHandled; /

                   switch(dwMsgMapID) /

                   { /

#define MESSAGE_HANDLER(msg, func) /

         if(uMsg == msg) /

         { /

                   bHandled = TRUE; /

                   lResult = func(uMsg, wParam, lParam, bHandled); /

                   if(bHandled) /

                            return TRUE; /

         }

#define END_MSG_MAP() /

                            break; /

                   default: /

                            ATLTRACE2(atlTraceWindowing, 0, _T("Invalid message map ID (%i)/n"), dwMsgMapID); /

                            ATLASSERT(FALSE); /

                            break; /

                   } /

                   return FALSE; /

         }

 

原來BEGIN_MSG_MAP等函數就是用展開ProcessWindowMessage函數的,而ProcessWindowMessage又是爲用來實現WinProc函數的,這下消息的流動過程就很清楚了。

 

另外與消息處理函數的兩個類是CmessageFilterCidleHandler,兩者都是接口類,各定義了一個純虛函數。在子類裏實現它們,然後註冊到CMessageLoop中去,CMessageLoop會在適當的時候調用它們。前者一般來轉換快捷鍵,後者一般用來更新界面。

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