不管。我现在要仿真出消息的流动循环路线--我常喜欢称之为消息的“二万五千里长征”。
消息如果是从子类流向父类(纵向流动),那么事情再简单不过,整个Message Map消息映射表已规划出十分明确的路线。但是正如卜一节一开始我说的,MFC之中用来处
理消息的C++类并不旱单线发展,作为application framework的重要结构之一的document/view,也具有处理消息的能力(你现在可能还不清楚什么是document/view,没
有关系),因此,消息应该有横向流动的机会。MFC对于消息循环的规定是:
1)如果是一般的Window、消息(WM xxx,则一定是由派生类流向基类, 没有旁流的可能。
2) 如果是命令消息WM COMMAND,那就有奇特的路线了:
图(1)MFC对于命令消息WM_COMMAND的特殊处理
不管这个规则是怎么定下来的,现在我要设计一个推动引擎,把它仿真出来。以下这些函数名称以及函数内容,完全仿真MFC内部。有些函数似乎赘余,那是因为我删掉了
许多卞题以外的操作。不把看似赘余的函数拿掉或合并,是为了留下MFC的足迹。此外,为了追踪调用过程(call stack),我在各函数的第一行输出一串识别文字。
首先我把新增加的一些成员函数做个列表:
全局函数AfxWhdPrn。就是我所谓的推动引擎的起始点。它本来应该是在CWihThread::Ruh中被调用,但为了实验目的,我在main中调用它,每调用一次便推送
一个消息。这个函数在1V}'C中有四个参数,为了方便,我加卜第五个,用以表示是谁获得消息(成为循环的起点)。例如:
AfxWndProc(0,WM CREATE,0,0,pMyFrame);
表示pM夕Fram。获得了一个WM_CREATE,而:
AfxWndProc(0,WM COMMAND,0,0,pMyView);
表示pMyView获得了一个WM_COMMAND
下面是消息的传递过程:
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, CWnd *pWnd) // last param. pWnd is added by JJHou. { cout << "AfxWndProc()" << endl; return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); } LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { cout << "AfxCallWndProc()" << endl; LRESULT lResult = pWnd->WindowProc(nMsg, wParam, lParam); return lResult; }
虽然殊途同归,但意义却是不相同的。切记!切记!
CWhd::WihdnwPrn。首先判断消息是否为WM COMMAND。如果不是,事情最单纯,就把消息往父类推去,父类再往祖父类推去。每到一个类的消息映射表,原本应该比较
AFX_MSGMAP_ENTRY的每一个元素,比较成功就调用对应的处理程序。
不过在这里我不作比较,只是把AFX_MSGMAP_ENTRY中的类识别代码印出来(就像上一节的Frame7程序一样),以表示“到此一游”:
如果消息是WM COMMAND } CWhd::WihdnwPrn。调用} OhCnmmahd。好,注意这又是一个CW}d的虚函数:
1.如果thi、指向CMyFrameWhd对象,那么调用的是CFrameWhd::OhCnmmahd
2.如果thi、指向CMyView对象,那么调用的是CView::OhCnmmahd。而因为
CView并没有改写OhCnmmahd,所以调用的其实是CWhd: :OhCnmmahd,这次可就没有殊途同归了。
消息的传递路线,如下:
Frame8测试四种情况:分别从frame对象和view 对象中推动消息,消息一般分为 Windows消息和 WM_COMMADND两种:
// test Message Routing AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame); AfxWndProc(0, WM_PAINT, 0, 0, pMyView); AfxWndProc(0, WM_COMMAND, 0, 0, pMyView); AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);效果如下:
CWinApp::InitApplication CMyWinApp::InitInstance CFrameWnd::Create CWnd::CreateEx CFrameWnd::PreCreateWindow CWinApp::Run CWinThread::Run pMyFrame receive WM_CREATE, routing path and call stack: AfxWndProc() AfxCallWndProc() CWnd::WindowProc() 1211 CMyFrameWnd 121 CFrameWnd 12 CWnd 1 CCmdTarget pMyView receive WM_PAINT, routing path and call stack: AfxWndProc() AfxCallWndProc() CWnd::WindowProc() 1221 CMyView 122 CView 12 CWnd 1 CCmdTarget pMyView receive WM_COMMAND, routing path and call stack: AfxWndProc() AfxCallWndProc() CWnd::WindowProc() CWnd::OnCommand() CView::OnCmdMsg() CCmdTarget::OnCmdMsg() 1221 CMyView 122 CView 12 CWnd 1 CCmdTarget CDocument::OnCmdMsg() CCmdTarget::OnCmdMsg() 131 CMyDoc 13 CDocument 1 CCmdTarget CWnd::DefWindowProc() pMyFrame receive WM_COMMAND, routing path and call stack: AfxWndProc() AfxCallWndProc() CWnd::WindowProc() CFrameWnd::OnCommand() CWnd::OnCommand() CFrameWnd::OnCmdMsg() CFrameWnd::GetActiveView() CView::OnCmdMsg() CCmdTarget::OnCmdMsg() 1221 CMyView 122 CView 12 CWnd 1 CCmdTarget CDocument::OnCmdMsg() CCmdTarget::OnCmdMsg() 131 CMyDoc 13 CDocument 1 CCmdTarget CCmdTarget::OnCmdMsg() 1211 CMyFrameWnd 121 CFrameWnd 12 CWnd 1 CCmdTarget CCmdTarget::OnCmdMsg() 1111 CMyWinApp 111 CWinApp 1 CCmdTarget CWnd::DefWindowProc() Press any key to continue
//mfc.h
#define TRUE 1 #define FALSE 0 typedef char* LPSTR; typedef const char* LPCSTR; typedef unsigned long DWORD; typedef int BOOL; typedef unsigned char BYTE; typedef unsigned short WORD; typedef int INT; typedef unsigned int UINT; typedef long LONG; typedef UINT WPARAM; typedef LONG LPARAM; typedef LONG LRESULT; typedef int HWND; #define WM_COMMAND 0x0111 // following windows.h #define WM_CREATE 0x0001 #define WM_PAINT 0x000F #define WM_NOTIFY 0x004E #define CObjectid 0xffff #define CCmdTargetid 1 #define CWinThreadid 11 #define CWinAppid 111 #define CMyWinAppid 1111 #define CWndid 12 #define CFrameWndid 121 #define CMyFrameWndid 1211 #define CViewid 122 #define CMyViewid 1221 #define CDocumentid 13 #define CMyDocid 131 #include <iostream.h> //////////////////////////////////////////////////////////////////// // Window message map handling struct AFX_MSGMAP_ENTRY; struct AFX_MSGMAP { AFX_MSGMAP* pBaseMessageMap; AFX_MSGMAP_ENTRY* lpEntries; }; #define DECLARE_MESSAGE_MAP() \ static AFX_MSGMAP_ENTRY _messageEntries[]; \ static AFX_MSGMAP messageMap; \ virtual AFX_MSGMAP* GetMessageMap() const; #define BEGIN_MESSAGE_MAP(theClass, baseClass) \ AFX_MSGMAP* theClass::GetMessageMap() const \ { return &theClass::messageMap; } \ AFX_MSGMAP theClass::messageMap = \ { &(baseClass::messageMap), \ (AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) }; \ AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \ { #define END_MESSAGE_MAP() \ { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; // Message map signature values and macros in separate header #include "afxmsg_.h" class CObject { public: CObject::CObject() { } CObject::~CObject() { } }; class CCmdTarget : public CObject { public: CCmdTarget::CCmdTarget() { } CCmdTarget::~CCmdTarget() { } virtual BOOL OnCmdMsg(UINT nID, int nCode); DECLARE_MESSAGE_MAP() // base class - no {{ }} macros }; typedef void (CCmdTarget::*AFX_PMSG)(void); struct AFX_MSGMAP_ENTRY // MFC 4.0 format { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) }; class CWinThread : public CCmdTarget { public: CWinThread::CWinThread() { } CWinThread::~CWinThread() { } virtual BOOL InitInstance() { cout << "CWinThread::InitInstance \n"; return TRUE; } virtual int Run() { cout << "CWinThread::Run \n"; // AfxWndProc(...); return 1; } }; class CWnd; class CWinApp : public CWinThread { public: CWinApp* m_pCurrentWinApp; CWnd* m_pMainWnd; public: CWinApp::CWinApp() { m_pCurrentWinApp = this; } CWinApp::~CWinApp() { } virtual BOOL InitApplication() { cout << "CWinApp::InitApplication \n"; return TRUE; } virtual BOOL InitInstance() { cout << "CWinApp::InitInstance \n"; return TRUE; } virtual int Run() { cout << "CWinApp::Run \n"; return CWinThread::Run(); } DECLARE_MESSAGE_MAP() }; typedef void (CWnd::*AFX_PMSGW)(void); // like 'AFX_PMSG' but for CWnd derived classes only class CDocument : public CCmdTarget { public: CDocument::CDocument() { } CDocument::~CDocument() { } virtual BOOL OnCmdMsg(UINT nID, int nCode); DECLARE_MESSAGE_MAP() }; class CWnd : public CCmdTarget { public: CWnd::CWnd() { } CWnd::~CWnd() { } virtual BOOL Create(); BOOL CreateEx(); virtual BOOL PreCreateWindow(); virtual LRESULT WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam); virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam); virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() }; class CView; class CFrameWnd : public CWnd { public: CView* m_pViewActive; // current active view public: CFrameWnd::CFrameWnd() { } CFrameWnd::~CFrameWnd() { } BOOL Create(); virtual BOOL PreCreateWindow(); CView* GetActiveView() const; virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); virtual BOOL OnCmdMsg(UINT nID, int nCode); DECLARE_MESSAGE_MAP() friend CView; }; class CView : public CWnd { public: CDocument* m_pDocument; public: CView::CView() { } CView::~CView() { } virtual BOOL OnCmdMsg(UINT nID, int nCode); DECLARE_MESSAGE_MAP() friend CFrameWnd; }; // global function CWinApp* AfxGetApp(); LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, CWnd* pWnd); // last param. pWnd is added by JJHOU. LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);
#include "my.h" extern CMyWinApp theApp; extern void printlpEntries(AFX_MSGMAP_ENTRY* lpEntry); BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode) { cout << "CCmdTarget::OnCmdMsg()" << endl; // Now look through message map to see if it applies to us AFX_MSGMAP* pMessageMap; AFX_MSGMAP_ENTRY* lpEntry; for (pMessageMap = GetMessageMap(); pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMessageMap) { lpEntry = pMessageMap->lpEntries; printlpEntries(lpEntry); } return FALSE; // not handled } BOOL CWnd::Create() { cout << "CWnd::Create \n"; return TRUE; } BOOL CWnd::CreateEx() { cout << "CWnd::CreateEx \n"; PreCreateWindow(); return TRUE; } BOOL CWnd::PreCreateWindow() { cout << "CWnd::PreCreateWindow \n"; return TRUE; } LRESULT CWnd::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { AFX_MSGMAP* pMessageMap; AFX_MSGMAP_ENTRY* lpEntry; cout << "CWnd::WindowProc()" << endl; if (nMsg == WM_COMMAND) // special case for commands { if (OnCommand(wParam, lParam)) return 1L; // 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, // otherwise should call DefWindowProc. // for simplization we just return 0. } LRESULT CWnd::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) { cout << "CWnd::DefWindowProc()" << endl; return TRUE; } BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam) { cout << "CWnd::OnCommand()" << endl; // ... return OnCmdMsg(0, 0); } BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam) { cout << "CFrameWnd::OnCommand()" << endl; // ... // route as normal command return CWnd::OnCommand(wParam, lParam); } BOOL CFrameWnd::Create() { cout << "CFrameWnd::Create \n"; CreateEx(); return TRUE; } BOOL CFrameWnd::PreCreateWindow() { cout << "CFrameWnd::PreCreateWindow \n"; return TRUE; } CView* CFrameWnd::GetActiveView() const { cout << "CFrameWnd::GetActiveView()" << endl; return m_pViewActive; } BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode) { cout << "CFrameWnd::OnCmdMsg()" << endl; // pump through current view FIRST CView* pView = GetActiveView(); if (pView->OnCmdMsg(nID, nCode)) return TRUE; // then pump through frame if (CWnd::OnCmdMsg(nID, nCode)) return TRUE; // last but not least, pump through app CWinApp* pApp = AfxGetApp(); if (pApp->OnCmdMsg(nID, nCode)) return TRUE; return FALSE; } BOOL CDocument::OnCmdMsg(UINT nID, int nCode) { cout << "CDocument::OnCmdMsg()" << endl; if (CCmdTarget::OnCmdMsg(nID, nCode)) return TRUE; return FALSE; } BOOL CView::OnCmdMsg(UINT nID, int nCode) { cout << "CView::OnCmdMsg()" << endl; if (CWnd::OnCmdMsg(nID, nCode)) return TRUE; BOOL bHandled = FALSE; bHandled = m_pDocument->OnCmdMsg(nID, nCode); return bHandled; } AFX_MSGMAP* CCmdTarget::GetMessageMap() const // JJHou: in MFC 40 cmdtarg.cpp { return &CCmdTarget::messageMap; } AFX_MSGMAP CCmdTarget::messageMap = // JJHou: in in MFC 40 cmdtarg.cpp { NULL, &CCmdTarget::_messageEntries[0] }; AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] = // JJHou: in in MFC 40 cmdtarg.cpp { { 0, 0, CCmdTargetid, 0, AfxSig_end, 0 } // nothing here }; BEGIN_MESSAGE_MAP(CWnd, CCmdTarget) ON_COMMAND(CWndid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CFrameWnd, CWnd) ON_COMMAND(CFrameWndid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CDocument, CCmdTarget) ON_COMMAND(CDocumentid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CView, CWnd) ON_COMMAND(CViewid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget) ON_COMMAND(CWinAppid, 0) END_MESSAGE_MAP() CWinApp* AfxGetApp() { return theApp.m_pCurrentWinApp; } LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, CWnd *pWnd) // last param. pWnd is added by JJHou. { cout << "AfxWndProc()" << endl; return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); } LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { cout << "AfxCallWndProc()" << endl; LRESULT lResult = pWnd->WindowProc(nMsg, wParam, lParam); return lResult; }
#include <iostream.h> #include "mfc.h" class CMyWinApp : public CWinApp { public: CMyWinApp::CMyWinApp() { } CMyWinApp::~CMyWinApp() { } virtual BOOL InitInstance(); DECLARE_MESSAGE_MAP() }; class CMyFrameWnd : public CFrameWnd { public: CMyFrameWnd(); ~CMyFrameWnd() { } DECLARE_MESSAGE_MAP() }; class CMyDoc : public CDocument { public: CMyDoc::CMyDoc() { } CMyDoc::~CMyDoc() { } DECLARE_MESSAGE_MAP() }; class CMyView : public CView { public: CMyView::CMyView() { } CMyView::~CMyView() { } DECLARE_MESSAGE_MAP() };
#include "my.h" CMyWinApp theApp; // global object BOOL CMyWinApp::InitInstance() { cout << "CMyWinApp::InitInstance \n"; m_pMainWnd = new CMyFrameWnd; return TRUE; } CMyFrameWnd::CMyFrameWnd() { Create(); } BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp) ON_COMMAND(CMyWinAppid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) ON_COMMAND(CMyFrameWndid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyDoc, CDocument) ON_COMMAND(CMyDocid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyView, CView) ON_COMMAND(CMyViewid, 0) END_MESSAGE_MAP() void printlpEntries(AFX_MSGMAP_ENTRY* lpEntry) { struct { int classid; char* classname; } classinfo[] = { CCmdTargetid , "CCmdTarget ", CWinThreadid , "CWinThread ", CWinAppid , "CWinApp ", CMyWinAppid , "CMyWinApp ", CWndid , "CWnd ", CFrameWndid , "CFrameWnd ", CMyFrameWndid, "CMyFrameWnd ", CViewid , "CView ", CMyViewid , "CMyView ", CDocumentid , "CDocument ", CMyDocid , "CMyDoc ", 0 , " " }; for (int i=0; classinfo[i].classid != 0; i++) { if (classinfo[i].classid == lpEntry->nID) { cout << lpEntry->nID << " "; cout << classinfo[i].classname << endl; break; } } } //------------------------------------------------------------------ // main //------------------------------------------------------------------ void main() { CWinApp* pApp = AfxGetApp(); pApp->InitApplication(); pApp->InitInstance(); pApp->Run(); CMyDoc* pMyDoc = new CMyDoc; CMyView* pMyView = new CMyView; CFrameWnd* pMyFrame = (CFrameWnd*)pApp->m_pMainWnd; pMyFrame->m_pViewActive = pMyView; pMyView->m_pDocument = pMyDoc; // test Message Routing cout << endl << "pMyFrame receive WM_CREATE, "; cout << "routing path and call stack:" << endl; AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame); cout << endl << "pMyView receive WM_PAINT, "; cout << "routing path and call stack:" << endl; AfxWndProc(0, WM_PAINT, 0, 0, pMyView); cout << endl << "pMyView receive WM_COMMAND, "; cout << "routing path and call stack:" << endl; AfxWndProc(0, WM_COMMAND, 0, 0, pMyView); cout << endl << "pMyFrame receive WM_COMMAND, "; cout << "routing path and call stack:" << endl; AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame); }
enum AfxSig { AfxSig_end = 0, // [marks end of message map] AfxSig_vv, }; #define ON_COMMAND(id, memberFxn) \ { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },