Windows靠消息的流动来维持运行。MFC里有消息映射表,这个消息映射表,把消息和处理消息的程序关联起来。
当我们的类库建立成功后,如果其中与消息相关的类(姑且叫做“消息标志类”,在MFC之中就是 CCmdTarget) 都是一条线式地继承,我们应该为每一个“消息标记类”准备一个消息映射表,并且将基类与派生类的消息映射表连接起来。然后,当窗口函数作消息的比较时,我们就可以想办法引导它沿着这条路走过去:
但是,MFC中用来处理消息的C++类,并不是呈单线发展。作为Application FrameWork的重要结构之一的Document/View,也具有处理消息的能力。消息不仅可以纵向流动(从子类到父类),还可以横向流动(从左兄弟到右兄弟)。
消息如何流动,我们暂时先不管。是直线前进,或是中途换跑道,我们都暂时不管,本案例先把消息的纵向流动,弄清楚。
这个消息的流动,就是消息映射表。也可以把它比作一张地图,或者一根多叉树。如果将消息与消息映射表中的元素进行比较,然后调用对应的处理程序,我们把这种比较就叫做“消息映射”(Message Mapping)。
MFC中消息的纵向流动,如图(3-5)所示:
为了验证整个消息映射表,我必须在映射表中做点记号,等全部构建完成之后,再一一追踪将记号显示出来。我将为每一个类的消息映射表加上这个项目:
ON_COMMAND(ClassId ,0)
这样就可以吧ClassId 镶嵌到映射表中作为记号。正式用途(与MFC中)当然不是这样,这只不过是权宜之计。
在main 函数中,我先产生4个对象(分别是 CMyWinApp、CMyFrameWnd、CMyDoc、CMyView 对象):
CMyWinApp theApp; //theApp是CMyWinApp对象 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; ... }
然后,分别取其消息映射表,一路追踪上去,把每一个消息映射表中的类的标记打印出来:
void main() { ... AFX_MSGMAP* pMessageMap = pMyView->GetMessageMap(); cout << endl << "CMyView Message Map : " << endl; MsgMapPrinting(pMessageMap); pMessageMap = pMyDoc->GetMessageMap(); cout << endl << "CMyDoc Message Map : " << endl; MsgMapPrinting(pMessageMap); pMessageMap = pMyFrame->GetMessageMap(); cout << endl << "CMyFrameWnd Message Map : " << endl; MsgMapPrinting(pMessageMap); pMessageMap = pApp->GetMessageMap(); cout << endl << "CMyWinApp Message Map : " << endl; MsgMapPrinting(pMessageMap); }自定义两个函数,来打印出消息映射表中的class标记:void MsgMapPrinting(AFX_MSGMAP* pMessageMap) 和 void printlpEntries(AFX_MSGMAP_ENTRY* lpEntry)
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; } } } void MsgMapPrinting(AFX_MSGMAP* pMessageMap) { for(; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMessageMap) { AFX_MSGMAP_ENTRY* lpEntry = pMessageMap->lpEntries; printlpEntries(lpEntry); } }
图(4)Frame7中消息的纵向流动
工程Frame7的详细代码如下:
//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; #define WM_COMMAND 0x0111 #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; // declared below after CWnd 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() { } DECLARE_MESSAGE_MAP() // base class - no {{ }} macros }; typedef void (CCmdTarget::*AFX_PMSG)(void); struct AFX_MSGMAP_ENTRY // MFC 4.0 { 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"; 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() { } DECLARE_MESSAGE_MAP() }; class CWnd : public CCmdTarget { public: CWnd::CWnd() { } CWnd::~CWnd() { } virtual BOOL Create(); BOOL CreateEx(); virtual BOOL PreCreateWindow(); DECLARE_MESSAGE_MAP() }; class CFrameWnd : public CWnd { public: CFrameWnd::CFrameWnd() { } CFrameWnd::~CFrameWnd() { } BOOL Create(); virtual BOOL PreCreateWindow(); DECLARE_MESSAGE_MAP() }; class CView : public CWnd { public: CView::CView() { } CView::~CView() { } DECLARE_MESSAGE_MAP() }; // global function CWinApp* AfxGetApp();
#include "my.h" // it should be mfc.h, but for CMyWinApp definition, so... extern CMyWinApp theApp; 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; } BOOL CFrameWnd::Create() { cout << "CFrameWnd::Create \n"; CreateEx(); return TRUE; } BOOL CFrameWnd::PreCreateWindow() { cout << "CFrameWnd::PreCreateWindow \n"; return TRUE; } CWinApp* AfxGetApp() { return theApp.m_pCurrentWinApp; } AFX_MSGMAP* CCmdTarget::GetMessageMap() const // JJHOU: in MFC 40 cmdtarg.cpp { return &CCmdTarget::messageMap; } AFX_MSGMAP CCmdTarget::messageMap = // JJHOU: in MFC 40 cmdtarg.cpp { NULL, &CCmdTarget::_messageEntries[0] }; AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] = // JJHOU: in in MFC 40 cmdtarg.cpp { // { 0, 0, 0, 0, AfxSig_end, 0 } // nothing here { 0, 0, CCmdTargetid, 0, AfxSig_end, 0 } }; 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()
#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; 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; } } } void MsgMapPrinting(AFX_MSGMAP* pMessageMap) { for(; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMessageMap) { AFX_MSGMAP_ENTRY* lpEntry = pMessageMap->lpEntries; printlpEntries(lpEntry); } } //------------------------------------------------------------------ // 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; // output Message Map construction AFX_MSGMAP* pMessageMap = pMyView->GetMessageMap(); cout << endl << "CMyView Message Map : " << endl; MsgMapPrinting(pMessageMap); pMessageMap = pMyDoc->GetMessageMap(); cout << endl << "CMyDoc Message Map : " << endl; MsgMapPrinting(pMessageMap); pMessageMap = pMyFrame->GetMessageMap(); cout << endl << "CMyFrameWnd Message Map : " << endl; MsgMapPrinting(pMessageMap); pMessageMap = pApp->GetMessageMap(); cout << endl << "CMyWinApp Message Map : " << endl; MsgMapPrinting(pMessageMap); }
//afxmsg_.h
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 },