您的位置:首页 > 其它

WTL 学习笔记 -- 消息流

2006-04-11 20:45 218 查看
WTL 学习笔记 -- 消息流

正如刚从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就是用作消息循环的类,_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函数的,这下消息的流动过程就很清楚了。

另外与消息处理函数的两个类是CmessageFilter和CidleHandler,两者都是接口类,各定义了一个纯虚函数。在子类里实现它们,然后注册到CMessageLoop中去,CMessageLoop会在适当的时候调用它们。前者一般来转换快捷键,后者一般用来更新界面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: