瑞星卡卡安全论坛

首页 » 技术交流区 » 系统软件 » MFC代码分析02:初探MFC程序消息处理机制
天下奇才 - 2008-9-28 15:19:00
上一篇:MFC代码分析01:初探MFC程序初始化及运行


经典的Windows版Hello World,当消息产生,系统就会把消息放到应用程序消息队列中,应用程序通过GetMessage取出消息内容,并通过DispatchMessage把消息交换给操作系统。此时,窗口消息回调函数就会被激活,去处理这一消息。

然而,在MFC中,消息是不是按照这样的机制进行中转处理呢?答案是不是的。为什么呢?虽然多态的特性,允许子类去改写一函数,使得在实际使用中使用子类的函数而不是父类的函数。但这样的代价就是背负着一个虚函数表。MFC各类的关系是一个复杂的树状结构,我们知道,如果一旦通过子类重写父类消息响应函数取实现消息的中转,那么每一个MFC类都要背负一个沉重的虚函数表,这样来说,对资源是一种极大的浪费。
MFC消息处理采取消息映射的策略进行实现:在每一个能接收和处理消息的类中,定义一个消息和响应函数的映射表。当接收到消息时,程序从消息表中检索是否存在该消息及其绑定的响应函数,如果存在则将消息交由该响应函数处理。

我们用一个最简单的例子进行说明,我们以WM_CREATE消息为例进行说明。当我们用向导创建一个WM_CREATE的响应函数时,VC便会为你在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间创建一段代码:
BEGIN_MESSAGE_MAP()
...
ON_WM_CREATE()
...
END_MESSAGE_MAP()

我们看一看,这一个ON_WM_CREATE()是个什么东西。转到其定义,我们发现,其实它是一个宏,其位于afxmsg_.h中:


引用:

#define ON_WM_CREATE() \
{ WM_CREATE, 0, 0, 0, AfxSig_is, \
  (AFX_PMSG) (AFX_PMSGW) \
  (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },

我们发现,这些宏进行的就是一个静态绑定的过程。以下是其中另外一些宏的含义:


引用:

AfxSig_is:枚举值,表示一函数指针类型 int (LPTSTR),存储于Entries in a message map (a 'AFX_MSGMAP_ENTRY') table中
AFX_PMSG: typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
AFX_PMSGW:typedef void (AFX_MSG_CALL CWnd::*AFX_PMSGW)(void);

我们在同一文件中也能发现一些最基本的系统消息的定义。


我们采用跟踪的方法,跟踪WM_CREATE消息,看看MFC的消息到底是怎么中转的。我们发现,其轨迹如下:
AfxWndProcBase --> AfxCallWndProc --> CWnd::WindowProc --> CWnd::OnWndMsg --> 消息处理函数


引用:

AfxWndProcBase,AfxCallWndProc(消息中转通道)
作用:将消息转至窗口消息回调函数

CWnd::WindowProc(主消息回调函数)
转交CWnd::OnWndMsg

CWnd::OnWndMsg
其中有个MessageMapFunctions结构,表示消息映射数据成员。进行消息的中转



用户系统信息:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322)
天下奇才 - 2008-9-28 15:19:00
相关代码


CWnd::WindowProc


引用:

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam = 0, LPARAM lParam = 0)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
MSG oldState = pThreadState->m_lastSentMsg;  // save for nesting
pThreadState->m_lastSentMsg.hwnd = hWnd;
pThreadState->m_lastSentMsg.message = nMsg;
pThreadState->m_lastSentMsg.wParam = wParam;
pThreadState->m_lastSentMsg.lParam = lParam;
#ifdef _DEBUG
_AfxTraceMsg(_T("WndProc"), &pThreadState->m_lastSentMsg);
#endif
// Catch exceptions thrown outside the scope of a callback
// in debug builds and warn the user.
LRESULT lResult;
TRY
{
#ifndef _AFX_NO_OCC_SUPPORT
  // special case for WM_DESTROY
  if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
  pWnd->m_pCtrlCont->OnUIActivate(NULL);   
#endif
  // special case for WM_INITDIALOG
  CRect rectOld;
  DWORD dwStyle = 0;
  if (nMsg == WM_INITDIALOG)
  _AfxPreInitDialog(pWnd, &rectOld, &dwStyle);
  // delegate to object's WindowProc
  lResult = pWnd->WindowProc(nMsg, wParam, lParam);
  // more special case for WM_INITDIALOG
  if (nMsg == WM_INITDIALOG)
  _AfxPostInitDialog(pWnd, rectOld, dwStyle);
}
CATCH_ALL(e)
{
  lResult = AfxProcessWndProcException(e, &pThreadState->m_lastSentMsg);
  TRACE(traceAppMsg, 0, "Warning: Uncaught exception in WindowProc (returning %ld).\n",
  lResult);
  DELETE_EXCEPTION(e);
}
END_CATCH_ALL
pThreadState->m_lastSentMsg = oldState;
return lResult;
}


CWnd::WindowProc


引用:

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult))
  lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}


CWnd::OnWndMsg


引用:

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
LRESULT lResult = 0;
union MessageMapFunctions mmf;
mmf.pfn = 0;
检查是否特殊消息
映射至消息处理函数   
return TRUE;
}
1
查看完整版本: MFC代码分析02:初探MFC程序消息处理机制