我们已经在上一节把整个消息流动网架设起来了。当消息进来,会有一个泵推动它前
进。消息如何进来,以及泵函数如何推动,都是属于Windows 程序设计的范畴,暂
时不管。我现在要解释消息的流动绕行路线。
消息如果是从子类别流向父类别(纵向流动),那么事情再简单不过,整个Message Map
消息映射表已规划出十分明确的路线。但是正如上一节一开始我说的,MFC 之中用来处
理消息的C++ 类别并不呈单鞭发展,作为application framework 的重要架构之一的
document/view,也具有处理消息的能力(你现在可能还不清楚什么是document/view,没
有关系);因此,消息应该有横向流动的机会。MFC 对于消息绕行的规定是:
1. 如果是一般的Windows 消息(WM_xxx),一定是由衍生类别流向基础类别,
没有旁流的可能。
2. 如果是命令消息WM_COMMAND,就有奇特的路线了:
如图所示:
如果消息是Document接收,则Document的处理函数OnCmdMsg先在本身匹配消息处理函数,没有则向Document template中去匹配。
如果消息是View接收,则View的处理函数OnCmdMsg先在本身匹配消息处理函数,没有则调用Document的OnCmdMsg去匹配。
如果消息是Frame窗口接收,则在Frame的处理函数中先调用View的OnCmdMsg去匹配。
COMMAND消息处理的绕行路线的实现主要使用了虚函数的特性和相互之间的调用。我这里不做详细讲解。下面只讲解COMMAND消息是如何到达Frame窗口或View窗口或Document窗口的。
全域函数 AfxWndProc 就是我所谓的推动引擎的起始点。它本来应该是在
CWinThread::Run中被调用,但为了讲解目的,我在main 中调用它,每调用一次便推送一个消息。这个函数在MFC 中有四个参数,为了方便,我加上第五个,用以表示是谁获得消息(成为绕行的起点)。例如:
AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);
表示pMyFrame 获得了一个WM_CREATE,而
AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);
表示pMyView 获得了一个WM_COMMAND。
下面是消息流动的过程:
LRESULTAfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, CWnd*pWnd) // last param. pWnd is added by JJHou. { cout<< "AfxWndProc()" << endl; returnAfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); } LRESULTAfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAMwParam, LPARAM lParam) { cout<< "AfxCallWndProc()" << endl; LRESULTlResult = pWnd->WindowProc(nMsg, wParam, lParam); returnlResult; }
pWnd->WindowProc究竟是调用哪一个函数?不一定,得视pWnd 到底指向何种类别之对象而定-- 别忘了WindowProc 是虚拟函数。这正是虚拟函数发挥它功效的地方呀:
如果pWnd 指向CMyFrameWnd对象,那么调用的是CFrameWnd::WindowProc。而因为CFrameWnd 并没有改写WindowProc ,所以调用的其实是CWnd::WindowProc。
如果pWnd 指向CMyView 对象,那么调用的是CView::WindowProc。而因为CView并没有改写WindowProc,所以调用的其实是CWnd::WindowProc。
虽然殊途同归,意义上是不相同的。切记!切记!
CWnd::WindowProc首先判断消息是否为WM_COMMAND。如果不是,事情最单纯,
就把消息往父类别推去,父类别再往祖父类别推去。每到一个类别的消息映射表,原本应该比对AFX_MSGMAP_ENTRY 的每一个元素,比对成功就调用对应的处理例程。不过在这里我不作比对,只是把AFX_MSGMAP_ENTRY 中的类别识别代码印出来(就像上一节一样),以表示「到此一游」:
LRESULTCWnd::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { AFX_MSGMAP*pMessageMap; AFX_MSGMAP_ENTRY*lpEntry; if (nMsg== WM_COMMAND) // special case for commands { if(OnCommand(wParam, lParam)) //在这个函数里面实现了对COMMAND消息的特//殊路线处理 return1L; // command handled else return(LRESULT)DefWindowProc(nMsg, wParam, lParam); } //普通消息就纵向匹配罢了 pMessageMap= GetMessageMap(); for (;pMessageMap != NULL; pMessageMap= pMessageMap->pBaseMessageMap) { lpEntry =pMessageMap->lpEntries; printlpEntries(lpEntry); } return 0;// J.J.Hou: if find, should call lpEntry->pfn, // otherwiseshould call DefWindowProc. // forsimplification, we just return 0. }