MFC剖析:消息机制 出处:白云黄鹤 转载的一篇老文,有的放矢的说明了MFC消息流程。 首先,让我们看一下MFC的消息循环部分:(程序取自MFC源程序,由于篇幅,我删去了一些非重要的部分。) MFC的WinMain函数:

extern "C" int WINAPI  
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,  
LPTSTR lpCmdLine, int nCmdShow)  
{  
// call shared/exported WinMain  
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);  
}  

int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,  

LPTSTR lpCmdLine, int nCmdShow)  
{  
int nReturnCode = -1;  
CWinApp* pApp = AfxGetApp();  

// ......  

// Perform specific initializations  
if (!pApp->InitInstance())  
{  
// ......  

//初始化实例不成功,通常一个Dialog Based MFC程序必须返回FALSE  
//这样就可以跳过消息循环。  
nReturnCode = pApp->ExitInstance();  
goto InitFailure;  
}  

nReturnCode = pApp->Run(); //进入消息循环部分  

InitFailure:  
// ......  

// 程序结束  
AfxWinTerm();  
return nReturnCode;  
}  

int CWinApp::Run()  
{  
// ......  
return CWinThread::Run(); // 消息循环被封装在CWinThread类里。  
}  

int CWinThread::Run()  
{  
BOOL bIdle = TRUE;  
LONG lIdleCount = 0;  

// 死循环,只有收到 WM_QUIT 消息后才会退出。  
for (;;)  
{  
while (bIdle &&  
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))  
{  
if (!OnIdle(lIdleCount++))  
bIdle = FALSE;  
}  
// 如果消息队列中没有消息,那么就调用OnIdle函数  
// 否则,发送消息  
do  
{  
if (!PumpMessage())  
// PumpMessage函数仅在收到WM_QUIT消息才返回FALSE  
return ExitInstance(); // 退出死循环  

if (IsIdleMessage(&m_msgCur))  
{  
bIdle = TRUE;  
lIdleCount = 0;  
}  
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));  
// 这段程序不仅完成了消息的发送,还实现了Idle功能。  
// GetMessage函数在消息队列中没有消息时,将不会返回,  
// 而是将控制权交给操作系统,直到消息队列中有消息为止。  
// 这段程序在一开始就调用PeekMessage函数来检测消息队列中  
// 是否有消息存在,如果存在就发送消息,  
// 否则就意味着空闲,那么就调用OnIdle函数,  
// 这样做,控制权永远不会交给操作系统。  
// 由于Windows 95, NT都是抢占式的操作系统,  
// 系统会自动进行任务切换。  
// 所以不用担心别的程序不会被运行。  
}  
}  

BOOL CWinThread::PumpMessage()  
{  
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))  
{// 收到 WM_QUIT 消息,就返回 FALSE。  
return FALSE;  
}  

// 否则就发送消息  
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)
)  
{  
::TranslateMessage(&m_msgCur);  
::DispatchMessage(&m_msgCur);  
}  
return TRUE;  
}  
</pre>

主程序的流程:

<pre lang="text">(程序开始)  
|  
|  
v  
WinMain  
|  
|  
v  
AfxWinMain  
|  
|  
v FALSE  
CWinApp::InitInstance-------> 退出程序  
|  
|TRUE  
|  
v  
CWinApp::Run  
|  
|  
v  
CWinThread::Run  
|  
|<----------------------------+ 
v FALSE | 
PeekMessage------->OnIdle--------+  
|TRUE |  
|<-------------------------+ | 
v | | 
GetMessage | | 
| | | 
| | | 
YES v | | 
+-----WM_QUIT 消息? | | 
| | | | 
| |NO | | 
| v | | 
| TranslateMessage | | 
| | | | 
| | | | 
| v | | 
| DispatchMessage | | 
| | | | 
| | | | 
| v TRUE | | 
| PeekMessage------------------>+ |  
| | |  
| |FALSE |  
| +-----------------------------+  
|  
v  
(程序结束)
</pre>

现在,再让我们来看一下MFC的窗口是如何响应消息的 我们先来看一段建立一个窗口的代码.

<pre lang="c">class CMsgWnd : public CWnd  
{  
public:  
CMsgWnd() {}  

// ClassWizard generated virtual function overrides  
//{{AFX_VIRTUAL(CMsgWnd)  
virtual BOOL Create(CWnd* pParentWnd);  
//}}AFX_VIRTUAL  

virtual ~CMsgWnd() {}  

protected:  
//{{AFX_MSG(CMsgWnd)  
afx_msg void OnPaint();  
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);  
//}}AFX_MSG  
DECLARE_MESSAGE_MAP()  
};  

BEGIN_MESSAGE_MAP(CMsgWnd, CWnd)  
//{{AFX_MSG_MAP(CMsgWnd)  
ON_WM_PAINT()  
ON_WM_LBUTTONDOWN()  
//}}AFX_MSG_MAP  
END_MESSAGE_MAP()  

BOOL CMsgWnd::Create(CWnd *pParentWnd)  
{  
return CWnd::Create(AfxRegisterWndClass(CS_DBLCLKS),  
"Message Window", WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),  
pParentWnd, 12345);  
}  

void CMsgWnd::OnPaint()  
{  
CPaintDC dc(this); // device context for painting  

dc.TextOut(0,0,"hello");  
}  

void CMsgWnd::OnLButtonDown(UINT nFlags, CPoint point)  
{  
MessageBox("OnLButtonDown");  

CWnd::OnLButtonDown(nFlags, point);  
}  
</pre>

以上是一段标准的CWnd窗口类的子类实现代码. 我想大家应该是可以看的懂的. 注意到CMsgWnd类中有一句代码 DECLARE_MESSAGE_MAP() 我们来看看这个宏是如何定义的:

<pre lang="c">typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);  
// 这是一个指向 CCmdTarget 类成员函数的指针类型.  

struct AFX_MSGMAP_ENTRY // 消息映射表之消息入口  
{  
UINT nMessage; // 消息  
UINT nCode; // 控制码或者是 WM_NOTIFY 消息的通知码  
UINT nID; // 控件的ID,如果是窗口消息则为0  
UINT nLastID; // 如果是一个范围的消息,那么这是最后一个控件的ID  
UINT nSig; // 消息处理类型  
AFX_PMSG pfn; // 消息处理函数  
};  

struct AFX_MSGMAP // 消息映射表  
{  
#ifdef _AFXDLL // 如果MFC是动态连接的,  
// 就是编译时选择 Use MFC in a shared DLL  
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();  
#else // MFC是静态连接的,编译时选择 Use MFC in a static library.  
const AFX_MSGMAP* pBaseMap;  
#endif  
// 如果MFC是动态连接的, 就用pfnGetBaseMap函数返回基类的消息映射表  
// 否则 pBaseBap 指向基类的消息映射表  

const AFX_MSGMAP_ENTRY* lpEntries; // 指向消息入口的指针  
};  

#ifdef _AFXDLL // 如果MFC是动态连接的  
#define DECLARE_MESSAGE_MAP() \  
private: \  
static const AFX_MSGMAP_ENTRY _messageEntries[]; \ // 消息入口  
protected: \  
static AFX_DATA const AFX_MSGMAP messageMap; \ // 消息映射表  
static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); \  
// 该函数返回基类的消息映射表  
virtual const AFX_MSGMAP* GetMessageMap() const; \  
// 该函数返回当前类的消息映射表  

#else // 静态连接的  
#define DECLARE_MESSAGE_MAP() \  
private: \  
static const AFX_MSGMAP_ENTRY _messageEntries[]; \  
protected: \  
static AFX_DATA const AFX_MSGMAP messageMap; \  
virtual const AFX_MSGMAP* GetMessageMap() const; \  
#endif  
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
// 以上这段代码实际上是嵌入在你的类中.  

 .CPP 文件中我们看到还有这段宏,  
BEGIN_MESSAGE_MAP(CMsgWnd, CWnd)  
//{{AFX_MSG_MAP(CMsgWnd)  
ON_WM_PAINT()  
ON_WM_LBUTTONDOWN()  
//}}AFX_MSG_MAP  
END_MESSAGE_MAP()  
现在我们再来看看它是被如何定义的.  

#ifdef _AFXDLL  
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \  
const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \  
{ return &baseClass::messageMap; } \ // 返回基类的消息映射表  
^^^^^^^^^^^^^^^^^^^^^^  
const AFX_MSGMAP* theClass::GetMessageMap() const \  
{ return &theClass::messageMap; } \ // 返回当前类的消息映射表  

AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ // 消息映射表  
{ &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \  
{ \  

#else  
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \  
const AFX_MSGMAP* theClass::GetMessageMap() const \  
{ return &theClass::messageMap; } \ // 返回当前类的消息映射表  

AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ // 消息映射表  
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \  
^^^^^^^^^^^^^^^^^^^^^^ 可以看到这是 MFC 动态连接与静态连接的区别所在  

动态连接时使用函数 _GetBaseMessageMap 返回 &baseClass::messageMap  
而静态连接是直接使用.至于Microsoft为什么要这样做,  
好像没有什么很好的理由.  
当然这个并不重要,我们暂且不用理会.  

const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \  
//开始初始化消息入口  
{ \  
#endif  

#define END_MESSAGE_MAP() \  
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \  
}; \ ^^^^^^^^^^^^^^^^^^^^^^ 指示消息映射结束.  

在这两个宏之间还有  
ON_WM_PAINT()  
ON_WM_LBUTTONDOWN()  
它们是我们在ClassWizard中选择了WM_PAINT和WM_LBUTTONDOWN消息后,  
MFC自动加入的,那么这两个又是如何定义的呢?  

......  

#define ON_WM_PAINT() \  
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \  
^^^^^^^^ ^ ^ ^ ^  
| | | | +------------- 消息处理类型  
| | | +------------------- LastID  
| | +---------------------- ID=0, 窗口消息  
| +------------------------- 控制码  
+-------------------------------- WM_PAINT 消息  
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)  
(void))&OnPaint },  
^^^^^^^^^^^^^^^ 消息处理函数  
.....  

#define ON_WM_LBUTTONDOWN() \  
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \  
^^^^^^^^^^^^^^ WM_LBUTTON 消息  
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)  
(UINT, CPoint))&OnLButtonDown },  
^^^^^^^^^^^^^ 消息处理函数  
......  

现在我们差不多可以看得出来了,消息处理函数可以  
_messageEntries来找到每个消息的处理函数.  
我们可以再看看CWnd类来验证我们的想法.  
先看一下窗口的建立过程:  
BOOL CWnd::Create(LPCTSTR lpszClassName,  
LPCTSTR lpszWindowName, DWORD dwStyle,  
const RECT& rect,  
CWnd* pParentWnd, UINT nID,  
CCreateContext* pContext)  
{  
// can't use for desktop or pop-up windows (use CreateEx instead)  
ASSERT(pParentWnd != NULL);  
ASSERT((dwStyle & WS_POPUP) == 0);  

return CreateEx(0, lpszClassName, lpszWindowName,  
dwStyle | WS_CHILD,  
rect.left, rect.top,  
rect.right - rect.left, rect.bottom - rect.top,  
pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);  
}  

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,  
LPCTSTR lpszWindowName, DWORD dwStyle,  
int x, int y, int nWidth, int nHeight,  
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)  
{  
// allow modification of several common create parameters  
CREATESTRUCT cs;  
cs.dwExStyle = dwExStyle;  
cs.lpszClass = lpszClassName;  
cs.lpszName = lpszWindowName;  
cs.style = dwStyle;  
cs.x = x;  
cs.y = y;  
cs.cx = nWidth;  
cs.cy = nHeight;  
cs.hwndParent = hWndParent;  
cs.hMenu = nIDorHMenu;  
cs.hInstance = AfxGetInstanceHandle();  
cs.lpCreateParams = lpParam;  

if (!PreCreateWindow(cs))  
{  
PostNcDestroy();  
return FALSE;  
}  

AfxHookWindowCreate(this);  
^^^^^^^^^^^^^^^^^^^^^^^^^^ 函数见后  
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,  
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,  
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);  

if (!AfxUnhookWindowCreate())  
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon  

if (hWnd == NULL)  
return FALSE;  
ASSERT(hWnd == m_hWnd); // should have been set in send msg hook  
return TRUE;  
}  

// for child windows  
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)  
{  
if (cs.lpszClass == NULL)  
{  
// make sure the default window class is registered  
if (!AfxDeferRegisterClass(AFX_WND_REG))  
return FALSE;  

// no WNDCLASS provided - use child window default  
ASSERT(cs.style & WS_CHILD);  
cs.lpszClass = _afxWnd;  
}  
return TRUE;  
}  

void AFXAPI AfxHookWindowCreate(CWnd* pWnd)  
{  
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  
if (pThreadState->m_pWndInit == pWnd)  
return;  

if (pThreadState->m_hHookOldCbtFilter == NULL)  
{  
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,  
^^^^^^  
Computer-based Training,当建立、删除、移动、最大化、最小化  
窗口时,将会调用钩子函数。  
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());  
^^^^^^^^^^^^^^^^^ 钩子函数  
if (pThreadState->m_hHookOldCbtFilter == NULL)  
AfxThrowMemoryException();  
}  
ASSERT(pThreadState->m_hHookOldCbtFilter != NULL);  
ASSERT(pWnd != NULL);  
ASSERT(pWnd->m_hWnd == NULL); // only do once  

ASSERT(pThreadState->m_pWndInit == NULL); // hook not already in progr
ess  
pThreadState->m_pWndInit = pWnd;  
}  

BOOL AFXAPI AfxUnhookWindowCreate()  
{  
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  
#ifndef _AFXDLL  
if (afxContextIsDLL && pThreadState->m_hHookOldCbtFilter != NULL)  
{  
::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
删除钩子函数  
pThreadState->m_hHookOldCbtFilter = NULL;  
}  
#endif  
if (pThreadState->m_pWndInit != NULL)  
{  
pThreadState->m_pWndInit = NULL;  
return FALSE; // was not successfully hooked  
}  
return TRUE;  
}  

// Window creation hooks  
LRESULT CALLBACK  
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)  
{  
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  
if (code != HCBT_CREATEWND)  
{ ^^^^^^^^^^^^^^^^^^^^^^ 是要建立窗口吗?  
// wait for HCBT_CREATEWND just pass others on...  
return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,  
wParam, lParam);  
}  

ASSERT(lParam != NULL);  
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;  
ASSERT(lpcs != NULL);  

// this hook exists to set the SendMessage hook on window creations  
// (but this is only done for MFC windows or non-child windows)  
// the subclassing cannot be done at this point because on Win32s  
// the window does not have the WNDPROC set yet  
CWnd* pWndInit = pThreadState->m_pWndInit;  
if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !afxContextIsDLL
))  
{  
ASSERT(wParam != NULL); // should be non-NULL HWND  
HWND hWnd = (HWND)wParam;  
WNDPROC oldWndProc;  
if (pWndInit != NULL)  
{ ^^^^^^^^^^^^^^^^ 窗口建立来自一个CWnd?  
// the window should not be in the permanent map at this time  
ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);  

// connect the HWND to pWndInit...  
pWndInit->Attach(hWnd);  
// allow other subclassing to occur first  
pWndInit->PreSubclassWindow();  

WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();  
ASSERT(pOldWndProc != NULL);  

#ifndef _MAC  
_AFX_CTL3D_STATE* pCtl3dState;  
DWORD dwFlags;  
if (!afxData.bWin4 && !afxContextIsDLL &&  
(pCtl3dState = _afxCtl3dState.GetDataNA()) != NULL &&  
pCtl3dState->m_pfnSubclassDlgEx != NULL &&  
(dwFlags = AfxCallWndProc(pWndInit, hWnd, WM_QUERY3DCONTROLS)) != 0)  

{  
// was the class registered with AfxWndProc?  
WNDPROC afxWndProc = AfxGetAfxWndProc();  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
函数见后  
BOOL bAfxWndProc = ((WNDPROC)  
GetWindowLong(hWnd, GWL_WNDPROC) == afxWndProc);  

pCtl3dState->m_pfnSubclassDlgEx(hWnd, dwFlags);  

// subclass the window if not already wired to AfxWndProc  
if (!bAfxWndProc)  
{  
// subclass the window with standard AfxWndProc  
oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,  
(DWORD)afxWndProc); // 修改窗口的WndProc!!!  

ASSERT(oldWndProc != NULL);  
*pOldWndProc = oldWndProc;  
}  
}  
else  
#endif  
{  
// subclass the window with standard AfxWndProc  
WNDPROC afxWndProc = AfxGetAfxWndProc();  
oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,  
(DWORD)afxWndProc);  
ASSERT(oldWndProc != NULL);  
if (oldWndProc != afxWndProc)  
*pOldWndProc = oldWndProc;  
}  
pThreadState->m_pWndInit = NULL;  
}  
else  
{  
ASSERT(!afxContextIsDLL); // should never get here  

// subclass the window with the proc which does gray backgrounds  
oldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);  
if (oldWndProc != NULL)  
{  
ASSERT(GetProp(hWnd, szAfxOldWndProc) == NULL);  
SetProp(hWnd, szAfxOldWndProc, oldWndProc);  
if ((WNDPROC)GetProp(hWnd, szAfxOldWndProc) == oldWndProc)  
{  
SetWindowLong(hWnd, GWL_WNDPROC,  
(DWORD)(pThreadState->m_bDlgCreate ?  
_AfxGrayBackgroundWndProc : _AfxActivationWndProc));  
ASSERT(oldWndProc != NULL);  
}  
}  
}  
}  

LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, co
de,  
wParam, lParam);  

#ifndef _AFXDLL  
if (afxContextIsDLL)  
{  
::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);  
pThreadState->m_hHookOldCbtFilter = NULL;  
}  
#endif  
return lResult;  
}  

// always indirectly accessed via AfxGetAfxWndProc  
WNDPROC AFXAPI AfxGetAfxWndProc()  
{  
#ifdef _AFXDLL  
return AfxGetModuleState()->m_pfnAfxWndProc;  
// 如果MFC是动态连入,返回的地址是AfxWndProcBase  
#else  
return &AfxWndProc; // 否则返回AfxWndProc  
#endif  
}  

// The WndProc for all CWnd's and derived classes  

LRESULT CALLBACK  
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)  
{  
// special message which identifies the window as using AfxWndProc  
if (nMsg == WM_QUERYAFXWNDPROC)  
^^^^^^^^^^^^^^^^^^ 查询是否为AFX的窗口过程  
return 1;  

// all other messages route through message map  
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
FromHandlePermanent函数用于返回一个和hWnd句柄对应的  
永久的CWnd类,所谓永久的,就是CWnd调用了Attach函数  
连上了一个窗口句柄。  

ASSERT(pWnd != NULL);  
ASSERT(pWnd->m_hWnd == hWnd);  
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 函数见后  
}  

#ifdef _AFXDLL  
#undef AfxWndProc  
LRESULT CALLBACK  
AfxWndProcBase(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)  
{  
AFX_MANAGE_STATE(_afxBaseModuleState.GetData());  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
// 由于MFC是动态连入的,所以必须要这条语句!  
return AfxWndProc(hWnd, nMsg, wParam, lParam);  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
调用MFC静态连入时使用的函数  
}  
#endif  

// 微软官方规定的发送消息给CWnd的函数  
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  
if (afxTraceFlags & traceWinMsg)  
_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;  
if (nMsg == WM_INITDIALOG)  
_AfxPreInitDialog(pWnd, &rectOld, &dwStyle);  

// delegate to object's WindowProc  
lResult = pWnd->WindowProc(nMsg, wParam, lParam);  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
调用CWnd类的WindowProc函数,函数见后  

// more special case for WM_INITDIALOG  
if (nMsg == WM_INITDIALOG)  
_AfxPostInitDialog(pWnd, rectOld, dwStyle);  
}  
CATCH_ALL(e)  
{  
lResult = AfxGetThread()->ProcessWndProcException(e, &pThreadState->m_
lastSentMsg);  
TRACE1("Warning: Uncaught exception in WindowProc (returning %ld).\n",

lResult);  
DELETE_EXCEPTION(e);  
}  
END_CATCH_ALL  

pThreadState->m_lastSentMsg = oldState;  
return lResult;  
}  
消息处理转入CWnd类了,  

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);  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
如果没有处理,就调用DefWindowProc  
呵呵,和我们以前用C写Windows程序一样!!  
return lResult;  
}  

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESUL
T* pResult)  
{  
LRESULT lResult = 0;  

// special case for commands  
if (message == WM_COMMAND)  
{ ^^^^^^^^^^ WM_COMMAND消息  
if (OnCommand(wParam, lParam))  
{ ^^^^^^^^^^^^^^^^^^^^^^^^^  
调用OnCommand函数,这是一个虚函数  
lResult = 1;  
goto LReturnTrue;  
}  
return FALSE;  
}  

// special case for notifies  
if (message == WM_NOTIFY)  
{ ^^^^^^^^^ 通告消息?  
NMHDR* pNMHDR = (NMHDR*)lParam;  
if (pNMHDR->hwndFrom != NULL &&  
OnNotify(wParam, lParam, &lResult))  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
调用OnNotify函数,也是一个虚函数  
goto LReturnTrue;  
return FALSE;  
}  

// special case for activation  
if (message == WM_ACTIVATE)  
_AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));  

// special case for set cursor HTERROR  
if (message == WM_SETCURSOR &&  
_AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))  
{  
lResult = 1;  
goto LReturnTrue;  
}  

const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
GetMessageMap返回当前类的消息映射表  

UINT iHash; iHash = (LOWORD((DWORD)pMessageMap) ^ message) & (iHashMax
-1);  
AfxLockGlobals(CRIT_WINMSGCACHE);  
AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
为了查找消息更快,微软实现了一个消息cache  
const AFX_MSGMAP_ENTRY* lpEntry;  
if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMa
p)  
{  
// 消息在cache里,命中  
lpEntry = pMsgCache->lpEntry;  
AfxUnlockGlobals(CRIT_WINMSGCACHE);  
if (lpEntry == NULL)  
return FALSE;  

// cache hit, and it needs to be handled  
if (message < 0xC000) 
goto LDispatch; 
else 
goto LDispatchRegistered; 
} 
else 
{ 
// 不在cache里面,只好查找整个消息映射表 
pMsgCache->nMsg = message;  
pMsgCache->pMessageMap = pMessageMap;  

#ifdef _AFXDLL  
for (/* pMessageMap already init'ed */; pMessageMap != NULL;  
pMessageMap = (*pMessageMap->pfnGetBaseMap)())  
#else  
for (/* pMessageMap already init'ed */; pMessageMap != NULL;  
pMessageMap = pMessageMap->pBaseMap)  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
从最高层类一层一层往下找  
#endif  
{  
// Note: catch not so common but fatal mistake!!  
// BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)  
#ifdef _AFXDLL  
ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());  
#else  
ASSERT(pMessageMap != pMessageMap->pBaseMap);  
#endif  

if (message < 0xC000) 
{ 
// constant window message 
if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,  
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
这是一个汇编写的查找消息处理入口的函数  
message, 0, 0)) != NULL)  
{  
pMsgCache->lpEntry = lpEntry;  
AfxUnlockGlobals(CRIT_WINMSGCACHE);  
goto LDispatch;  
}  
}  
else  
{  
// registered windows message  
lpEntry = pMessageMap->lpEntries;  
while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)

{  
UINT* pnID = (UINT*)(lpEntry->nSig);  
ASSERT(*pnID >= 0xC000 || *pnID == 0);  
// must be successfully registered  
if (*pnID == message)  
{  
pMsgCache->lpEntry = lpEntry;  
AfxUnlockGlobals(CRIT_WINMSGCACHE);  
goto LDispatchRegistered;  
}  
lpEntry++; // keep looking past this one  
}  
}  
}  

pMsgCache->lpEntry = NULL;  
AfxUnlockGlobals(CRIT_WINMSGCACHE);  
return FALSE;  
}  
ASSERT(FALSE); // not reached  

LDispatch: //消息处理函数找到了就转到此处了  
ASSERT(message < 0xC000); 
union MessageMapFunctions mmf; 
mmf.pfn = lpEntry->pfn;  

// if we've got WM_SETTINGCHANGE / WM_WININICHANGE, we need to  
// decide if we're going to call OnWinIniChange() or OnSettingChange()

int nSig;  
nSig = lpEntry->nSig;  
^^^^^^^^^^^^^^^^^^^^  
if (lpEntry->nID == WM_SETTINGCHANGE)  
{  
DWORD dwVersion = GetVersion();  
if (LOBYTE(LOWORD(dwVersion)) >= 4)  
nSig = AfxSig_vws;  
else  
nSig = AfxSig_vs;  
}  

switch (nSig)  
{// nSig为消息参数类型,详细解释见后  
default:  
ASSERT(FALSE);  
break;  

case AfxSig_bD:  
lResult = (this->*mmf.pfn_bD)(CDC::FromHandle((HDC)wParam));  
^^^ mmf就是我们在Message Map里面设的消息处理函数  
它是一个共用体(union),其中定义了  
带有各种不同参数形式的函数。  
break;  

case AfxSig_bb: // AfxSig_bb, AfxSig_bw, AfxSig_bh  
lResult = (this->*mmf.pfn_bb)((BOOL)wParam);  
break;  

case AfxSig_bWww: // really AfxSig_bWiw  
lResult = (this->*mmf.pfn_bWww)(CWnd::FromHandle((HWND)wParam),  
(short)LOWORD(lParam), HIWORD(lParam));  
break;  

case AfxSig_bWCDS:  
lResult = (this->*mmf.pfn_bWCDS)(CWnd::FromHandle((HWND)lParam),  
(COPYDATASTRUCT*) lParam);  
break;  

case AfxSig_bHELPINFO:  
lResult = (this->*mmf.pfn_bHELPINFO)((HELPINFO*)lParam);  
break;  

case AfxSig_hDWw:  
{  
// special case for OnCtlColor to avoid too many temporary objects  
ASSERT(message == WM_CTLCOLOR);  
AFX_CTLCOLOR* pCtl = (AFX_CTLCOLOR*)lParam;  
CDC dcTemp; dcTemp.m_hDC = pCtl->hDC;  
CWnd wndTemp; wndTemp.m_hWnd = pCtl->hWnd;  
UINT nCtlType = pCtl->nCtlType;  
// if not coming from a permanent window, use stack temporary  
CWnd* pWnd = CWnd::FromHandlePermanent(wndTemp.m_hWnd);  
if (pWnd == NULL)  
{  
#ifndef _AFX_NO_OCC_SUPPORT  
// determine the site of the OLE control if it is one  
COleControlSite* pSite;  
if (m_pCtrlCont != NULL && (pSite = (COleControlSite*)  
m_pCtrlCont->m_siteMap.GetValueAt(wndTemp.m_hWnd)) != NULL)  
{  
wndTemp.m_pCtrlSite = pSite;  
}  
#endif  
pWnd = &wndTemp;  
}  
HBRUSH hbr = (this->*mmf.pfn_hDWw)(&dcTemp, pWnd, nCtlType);  
// fast detach of temporary objects  
dcTemp.m_hDC = NULL;  
wndTemp.m_hWnd = NULL;  
lResult = (LRESULT)hbr;  
}  
break;  

case AfxSig_hDw:  
{  
// special case for CtlColor to avoid too many temporary objects  
ASSERT(message == WM_REFLECT_BASE+WM_CTLCOLOR);  
AFX_CTLCOLOR* pCtl = (AFX_CTLCOLOR*)lParam;  
CDC dcTemp; dcTemp.m_hDC = pCtl->hDC;  
UINT nCtlType = pCtl->nCtlType;  
HBRUSH hbr = (this->*mmf.pfn_hDw)(&dcTemp, nCtlType);  
// fast detach of temporary objects  
dcTemp.m_hDC = NULL;  
lResult = (LRESULT)hbr;  
}  
break;  

case AfxSig_iwWw:  
lResult = (this->*mmf.pfn_iwWw)(LOWORD(wParam),  
CWnd::FromHandle((HWND)lParam), HIWORD(wParam));  
break;  

case AfxSig_iww:  
lResult = (this->*mmf.pfn_iww)(LOWORD(wParam), HIWORD(wParam));  
break;  

case AfxSig_iWww: // really AfxSig_iWiw  
lResult = (this->*mmf.pfn_iWww)(CWnd::FromHandle((HWND)wParam),  
(short)LOWORD(lParam), HIWORD(lParam));  
break;  

case AfxSig_is:  
lResult = (this->*mmf.pfn_is)((LPTSTR)lParam);  
break;  

case AfxSig_lwl:  
lResult = (this->*mmf.pfn_lwl)(wParam, lParam);  
break;  

case AfxSig_lwwM:  
lResult = (this->*mmf.pfn_lwwM)((UINT)LOWORD(wParam),  
(UINT)HIWORD(wParam), (CMenu*)CMenu::FromHandle((HMENU)lParam));  
break;  

case AfxSig_vv:  
(this->*mmf.pfn_vv)();  
break;  

case AfxSig_vw: // AfxSig_vb, AfxSig_vh  
(this->*mmf.pfn_vw)(wParam);  
break;  

case AfxSig_vww:  
(this->*mmf.pfn_vww)((UINT)wParam, (UINT)lParam);  
break;  

case AfxSig_vvii:  
(this->*mmf.pfn_vvii)((short)LOWORD(lParam), (short)HIWORD(lParam));  

break;  

case AfxSig_vwww:  
(this->*mmf.pfn_vwww)(wParam, LOWORD(lParam), HIWORD(lParam));  
break;  

case AfxSig_vwii:  
(this->*mmf.pfn_vwii)(wParam, LOWORD(lParam), HIWORD(lParam));  
break;  

case AfxSig_vwl:  
(this->*mmf.pfn_vwl)(wParam, lParam);  
break;  

case AfxSig_vbWW:  
(this->*mmf.pfn_vbWW)(m_hWnd == (HWND)lParam,  
CWnd::FromHandle((HWND)lParam),  
CWnd::FromHandle((HWND)wParam));  
break;  

case AfxSig_vD:  
(this->*mmf.pfn_vD)(CDC::FromHandle((HDC)wParam));  
break;  

case AfxSig_vM:  
(this->*mmf.pfn_vM)(CMenu::FromHandle((HMENU)wParam));  
break;  

case AfxSig_vMwb:  
(this->*mmf.pfn_vMwb)(CMenu::FromHandle((HMENU)wParam),  
LOWORD(lParam), (BOOL)HIWORD(lParam));  
break;  

case AfxSig_vW:  
(this->*mmf.pfn_vW)(CWnd::FromHandle((HWND)wParam));  
break;  

case AfxSig_vW2:  
(this->*mmf.pfn_vW)(CWnd::FromHandle((HWND)lParam));  
break;  

case AfxSig_vWww:  
(this->*mmf.pfn_vWww)(CWnd::FromHandle((HWND)wParam), LOWORD(lParam), 

HIWORD(lParam));  
break;  

case AfxSig_vWp:  
{  
CPoint point((DWORD)lParam);  
(this->*mmf.pfn_vWp)(CWnd::FromHandle((HWND)wParam), point);  
}  
break;  

case AfxSig_vWh:  
(this->*mmf.pfn_vWh)(CWnd::FromHandle((HWND)wParam),  
(HANDLE)lParam);  
break;  

case AfxSig_vwW:  
(this->*mmf.pfn_vwW)(wParam, CWnd::FromHandle((HWND)lParam));  
break;  

case AfxSig_vwWb:  
(this->*mmf.pfn_vwWb)((UINT)(LOWORD(wParam)),  
CWnd::FromHandle((HWND)lParam), (BOOL)HIWORD(wParam));  
break;  

case AfxSig_vwwW:  
case AfxSig_vwwx:  
{  
// special case for WM_VSCROLL and WM_HSCROLL  
ASSERT(message == WM_VSCROLL || message == WM_HSCROLL ||  
message == WM_VSCROLL+WM_REFLECT_BASE || message == WM_HSCROLL+WM_REFL
ECT_BASE);  
int nScrollCode = (short)LOWORD(wParam);  
int nPos = (short)HIWORD(wParam);  
if (lpEntry->nSig == AfxSig_vwwW)  
(this->*mmf.pfn_vwwW)(nScrollCode, nPos,  
CWnd::FromHandle((HWND)lParam));  
else  
(this->*mmf.pfn_vwwx)(nScrollCode, nPos);  
}  
break;  

case AfxSig_vs:  
(this->*mmf.pfn_vs)((LPTSTR)lParam);  
break;  

case AfxSig_vws:  
(this->*mmf.pfn_vws)((UINT) wParam, (LPCTSTR)lParam);  
break;  

case AfxSig_vOWNER:  
(this->*mmf.pfn_vOWNER)((int)wParam, (LPTSTR)lParam);  
lResult = TRUE;  
break;  

case AfxSig_iis:  
lResult = (this->*mmf.pfn_iis)((int)wParam, (LPTSTR)lParam);  
break;  

case AfxSig_wp:  
{  
CPoint point((DWORD)lParam);  
lResult = (this->*mmf.pfn_wp)(point);  
}  
break;  

case AfxSig_wv: // AfxSig_bv, AfxSig_wv  
lResult = (this->*mmf.pfn_wv)();  
break;  

case AfxSig_vCALC:  
(this->*mmf.pfn_vCALC)((BOOL)wParam, (NCCALCSIZE_PARAMS*)lParam);  
break;  

case AfxSig_vPOS:  
(this->*mmf.pfn_vPOS)((WINDOWPOS*)lParam);  
break;  

case AfxSig_vwwh:  
(this->*mmf.pfn_vwwh)(LOWORD(wParam), HIWORD(wParam), (HANDLE)lParam);

break;  

case AfxSig_vwp:  
{  
CPoint point((DWORD)lParam);  
(this->*mmf.pfn_vwp)(wParam, point);  
break;  
}  
case AfxSig_vwSIZING:  
(this->*mmf.pfn_vwl)(wParam, lParam);  
lResult = TRUE;  
break;  

case AfxSig_bwsp:  
lResult = (this->*mmf.pfn_bwsp)(LOWORD(wParam), (short) HIWORD(wParam)
,  
CPoint(LOWORD(lParam), HIWORD(lParam)));  
if (!lResult)  
return FALSE;  
}  
goto LReturnTrue;  

LDispatchRegistered: // for registered windows messages  
ASSERT(message >= 0xC000);  
mmf.pfn = lpEntry->pfn;  
lResult = (this->*mmf.pfn_lwl)(wParam, lParam);  

LReturnTrue:  
if (pResult != NULL)  
*pResult = lResult;  
return TRUE;  
}  

LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)  

{  
if (m_pfnSuper != NULL)  
return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);  

WNDPROC pfnWndProc;  
if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)  
return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);  
else  
return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);  
}  

enum AfxSig // 消息处理函数的参数类型  
{  
AfxSig_end = 0, // [marks end of message map]  

AfxSig_bD, // BOOL (CDC*)  
^^  
|+--------------- D表示CDC*  
+---------------- b表示返回值为bool型  
AfxSig_bb, // BOOL (BOOL)  
AfxSig_bWww, // BOOL (CWnd*, UINT, UINT)  
AfxSig_hDWw, // HBRUSH (CDC*, CWnd*, UINT)  
AfxSig_hDw, // HBRUSH (CDC*, UINT)  
AfxSig_iwWw, // int (UINT, CWnd*, UINT)  
AfxSig_iww, // int (UINT, UINT)  
AfxSig_iWww, // int (CWnd*, UINT, UINT)  
AfxSig_is, // int (LPTSTR)  
AfxSig_lwl, // LRESULT (WPARAM, LPARAM)  
AfxSig_lwwM, // LRESULT (UINT, UINT, CMenu*)  
AfxSig_vv, // void (void)  
^^^^^^^^^ 举个例子,如WM_PAINT消息  
BEGIN_MESSAGE_MAP里面我们定义的消息处理函数为  
ON_WM_PAINT()  
察看我们前面剖析过程,知道  
#define ON_WM_PAINT() \  
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \  

所以处理函数的形式即为  
OnPaint()  
即没有参数,没有返回值。  
采用这种方法,就可以避免把没用的wParam和lParam参数传给用户  

AfxSig_vw, // void (UINT)  
AfxSig_vww, // void (UINT, UINT)  
AfxSig_vvii, // void (int, int) // wParam is ignored  
AfxSig_vwww, // void (UINT, UINT, UINT)  
AfxSig_vwii, // void (UINT, int, int)  
AfxSig_vwl, // void (UINT, LPARAM)  
AfxSig_vbWW, // void (BOOL, CWnd*, CWnd*)  
AfxSig_vD, // void (CDC*)  

AfxSig_vM, // void (CMenu*)  
AfxSig_vMwb, // void (CMenu*, UINT, BOOL)  

AfxSig_vW, // void (CWnd*)  
AfxSig_vWww, // void (CWnd*, UINT, UINT)  
AfxSig_vWp, // void (CWnd*, CPoint)  
AfxSig_vWh, // void (CWnd*, HANDLE)  
AfxSig_vwW, // void (UINT, CWnd*)  
AfxSig_vwWb, // void (UINT, CWnd*, BOOL)  
AfxSig_vwwW, // void (UINT, UINT, CWnd*)  
AfxSig_vwwx, // void (UINT, UINT)  
AfxSig_vs, // void (LPTSTR)  
AfxSig_vOWNER, // void (int, LPTSTR), force return TRUE  
AfxSig_iis, // int (int, LPTSTR)  
AfxSig_wp, // UINT (CPoint)  
AfxSig_wv, // UINT (void)  
AfxSig_vPOS, // void (WINDOWPOS*)  
AfxSig_vCALC, // void (BOOL, NCCALCSIZE_PARAMS*)  
AfxSig_vNMHDRpl, // void (NMHDR*, LRESULT*)  
AfxSig_bNMHDRpl, // BOOL (NMHDR*, LRESULT*)  
AfxSig_vwNMHDRpl, // void (UINT, NMHDR*, LRESULT*)  
AfxSig_bwNMHDRpl, // BOOL (UINT, NMHDR*, LRESULT*)  
AfxSig_bHELPINFO, // BOOL (HELPINFO*)  
AfxSig_vwSIZING, // void (UINT, LPRECT) -- return TRUE  

// signatures specific to CCmdTarget  
AfxSig_cmdui, // void (CCmdUI*)  
AfxSig_cmduiw, // void (CCmdUI*, UINT)  
AfxSig_vpv, // void (void*)  
AfxSig_bpv, // BOOL (void*)  

// Other aliases (based on implementation)  
AfxSig_vwwh, // void (UINT, UINT, HANDLE)  
AfxSig_vwp, // void (UINT, CPoint)  
AfxSig_bw = AfxSig_bb, // BOOL (UINT)  
AfxSig_bh = AfxSig_bb, // BOOL (HANDLE)  
AfxSig_iw = AfxSig_bb, // int (UINT)  
AfxSig_ww = AfxSig_bb, // UINT (UINT)  
AfxSig_bv = AfxSig_wv, // BOOL (void)  
AfxSig_hv = AfxSig_wv, // HANDLE (void)  
AfxSig_vb = AfxSig_vw, // void (BOOL)  
AfxSig_vbh = AfxSig_vww, // void (BOOL, HANDLE)  
AfxSig_vbw = AfxSig_vww, // void (BOOL, UINT)  
AfxSig_vhh = AfxSig_vww, // void (HANDLE, HANDLE)  
AfxSig_vh = AfxSig_vw, // void (HANDLE)  
AfxSig_viSS = AfxSig_vwl, // void (int, STYLESTRUCT*)  
AfxSig_bwl = AfxSig_lwl,  
AfxSig_vwMOVING = AfxSig_vwSIZING, // void (UINT, LPRECT) -- return TR
UE  

AfxSig_vW2, // void (CWnd*) (CWnd* comes from lParam)  
AfxSig_bWCDS, // BOOL (CWnd*, COPYDATASTRUCT*)  
AfxSig_bwsp, // BOOL (UINT, short, CPoint)  
AfxSig_vws,  
};