WTL 学习笔记 -- 消息流
2006-04-11 20:45
218 查看
WTL 学习笔记 -- 消息流
正如刚从DOS转到Win32下写程序时,总是为找不到main函数而感到不爽,学习时WTL时,第一反应是找GetMessage和DispatchMessage,想知道消息是如何分发到窗口的。
在_tWinMain里做了一些初始化函数之后,就进入了Run函数,显然Run函数就是消息循环。下面是Run函数的代码:
不出所料,CmessageLoop就是用作消息循环的类,_Module.AddMessageLoop不过是保存一个全局引用,其它地方可以方便的取得CmessageLoop的实例theLoop。
CmessageLoop::Run函数无疑就是消息循环函数了:
CmessageLoop::Run函数,不断的从消息队列里取消息,然后分发给对应的窗口。当消息分发到窗口时,自然就是调用窗口的WINPROC函数了。那么WINPROC是在哪里注册,在哪里实现的呢?
原来在窗口创建函数里注册WinClass,因为这是在基类里实现的,它需要从子类窗口中获得WinClass信息。很明显,这里调用GetWndClassInfo获得WinClass信息。但是我们并没有看到子类里实现该函数,应该是宏展开的吧,DECLARE_XXXX_WND_CLASS之类最为可疑:
没错,就是它,原来WINPROC函数名为StartWindowProc。问题又来了:StartWindowProc在哪里实现了,子类没有,自然是基类里了:
这个函数的实现有点晦涩,主要是不太明白thunk指的是什么。基本功能倒是很明显,只是重新设置了一下WINPROC,也就是说WINPROC被换成新的了,下次再也不会调用StartWindowProc了。很快找到了真正的WINPROC:
在这个函数里,调用ProcessWindowMessage去分发消息,ProcessWindowMessage是在哪里实现的呢?没有找到自然是宏展开的:
原来BEGIN_MSG_MAP等函数就是用展开ProcessWindowMessage函数的,而ProcessWindowMessage又是为用来实现WinProc函数的,这下消息的流动过程就很清楚了。
另外与消息处理函数的两个类是CmessageFilter和CidleHandler,两者都是接口类,各定义了一个纯虚函数。在子类里实现它们,然后注册到CMessageLoop中去,CMessageLoop会在适当的时候调用它们。前者一般来转换快捷键,后者一般用来更新界面。
正如刚从DOS转到Win32下写程序时,总是为找不到main函数而感到不爽,学习时WTL时,第一反应是找GetMessage和DispatchMessage,想知道消息是如何分发到窗口的。
在_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::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; } |
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); } |
#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; / } |
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); } |
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; } |
#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; / } |
另外与消息处理函数的两个类是CmessageFilter和CidleHandler,两者都是接口类,各定义了一个纯虚函数。在子类里实现它们,然后注册到CMessageLoop中去,CMessageLoop会在适当的时候调用它们。前者一般来转换快捷键,后者一般用来更新界面。
相关文章推荐
- WTL 学习笔记 -- 消息流
- WTL学习笔记——(5)工具条与状态条
- WTL学习笔记——(2)WTL Hello World !
- WTL 学习笔记 -- 几则技巧
- WTL 学习笔记 -- ActiveX
- WTL学习笔记——(8)ATL/WTL开源项目
- WTL学习笔记之"Enter"和"ESC"的一点小心得
- WTL学习笔记(1)基础
- WTL 学习笔记 -- DDX 和 DDV
- WTL学习笔记——(4)WTL界面基类
- WTL for MFC Programmers 学习笔记(二) Part II WTL GUI Base Classes
- WTL 学习笔记 -- 几则技巧
- WTL学习笔记(1)
- WTL 学习笔记 -- CSplitterWindow
- WTL学习笔记之NM_CUSTOMDRAW和WM_DRAWITEM
- WTL学习笔记(2)ToolBar && StatusBar
- WTL学习笔记(5)双缓冲技术和动画(BufferedPaint)
- WTL 学习笔记 -- CSplitterWindow
- WTL 学习笔记 -- ActiveX
- WTL学习笔记之NM_CUSTOMDRAW和WM_DRAWITEM