Windows程序靠消息的流动维护生命。消息的一般处理方式是在窗口函数中借助一个大大的switch/case比较操作,判别消息再调用对应的处理程序.为了简化比较操作,也让程序代码更模块化一些,提供了一种消息映射表做法,把消息和其处理程序关联起来。
当我们的类库成立后,若其中与消息有关的类(暂且叫“消息标志类”,MFC之中就是CCmdTarget)都是一条线式地继承,我们应该为每一个“消息标志类”准备一个消息映射表,并且将基类与派生类的消息映射表连接起来。当窗口函数作消息比较时,就引导它沿这条线走。 MFC中用来处理消息的C++类,并不呈单线发展。作为application framework的重要结构之一的document/view,也具有处理消息的能力。因此消息的遍历路线也有横流的机会。 消息如何流动,暂且先不管。我们先建立整个攀爬路线网就是所谓的消息映射表(Message Map).将消息和表格中元素比较,然后调用对应的处理程序,这种操作称为消息映射(Message Mapping). 为了尽量降低对正常(一般)类声明和定义的影响,希望最好能想RTTI和Dynamic Creation一样,用一两个宏就完成消息映射表的建构。 首先定义一个数据结构: struct AFX_MSGMAP { AFX_MAGMAP * pBaseMessageMap; AFX_MSGMAP_ENTRY * lpEnteries; } 其中AFX_MSGMAP_ENTRY的定义如下: 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 o for windows message) UINT nLastID; //used for entries specifying a range og control id ’ s UINT nSig; //signature type (action) or pointer to message AFX_PMSG pfn; /runtime to call ( or special value) } typedef void (CCmdTarget::*AFX_PMSG) (void) 然后我们定义一个宏: #define DECLARE_MESSAGE_MAP() / static AFX_MESSAGE_ENTRY – messageEntries[]; / static AFX_MSGMAP messageMap;/ virtual AFX_MSGMAP * GetMessageMap() const; DECLARE_MESSAGE_MAP的内容填充工作由三个宏完成: #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 ON_COMMAND (id,memberFxn) / (WM_COMMAND,0,(WORD)id, (WORD)id,AfxSig_vv,(AFX_PMSG)memberFxn), #define END_MESSAGE_MAP( )/ (0,0,0,0,AfxSig_end, (AFX_PMSG)0 ) / }; 其中的AfxSig_end定义为: enum AfxSig { AfxSig_end = 0, //{marks end of message map} AfxSig_vv, }; AfxSig_XX用来描述消息处理程序memberFxn的类型(参数和返回值)。 下面是以Cview为例的程序: class Cview : public CWnd { public: … DECLARE_MESSAGE_MAP() }; #define CviewId 122 … BEGIN_MESSAGE_MAP( Cview , CWnd ) ON_COMMAND( CviewId,0 ) END_MESSAGE_MAP() 上述代码展开后: class CView : public CWnd { public: … static AFX_MSGMAP_ENTRY _messAgeENtries(); static AFX_MSGMAP messageMap; virtual AFX_MSGMAP * GetMessageMap() const; }; AFX_MSGMAP * CView::GetMessageMap() const / { return &CView::messageMap; } / AFX_MSGMAP CView::messageMap = / (&(CWnd::messageMap),/ (AFX_MSGMAP_ENTRY *)&(CView::_messageEntries));/ AFX_MSGMAP_ENTRY CView::messageEntries[] = / { (WM_COMMAND,0,(WORD)122,(WORD)122,1,(AFX_PMSG)0 ), (0,0,0,0,AfxSig_end, (AFX_PMSG)0 ) }; 我们还可以定义各种类似ON_COMMAND这样的宏,把各式各样的消息与特定的处理程序关联起来。MFC中有ON_WN_PAINT,ON_WM_CREATE,ON_WM_SIZE … 等等。 范例程序 MFC.H #define TURE 1 #define FALSE 0 typedef const char * LPCSTR ; typedef char * LPSTR; 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 CMyWinApp 1111 #define CWndid 12 #define CFrameWndid 121 #define CMyFrameWndid 1211 #define CViewid 122 #define CMyViewid 1221 #define CDocument 13 #define CMyDocid 131 #include <iostream.h> struct AFX_MSGMAP { AFX_MAGMAP * pBaseMessageMap; AFX_MSGMAP_ENTRY * lpEnteries; } #define DECLARE_MESSAGE_MAP() / static AFX_MESSAGE_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 value and macros in separate header #include “ afxmsg_.h ” class CObject { public: CObject::CObject() { } CObject::~CObject() { } Virtual CRuntimeClass *GetRuntimeClass() const; BOOL IsKindOf(const CRuntimeClass *pCLass) const; Public: static CRuntimeClass classCObject; virtual void Sayhell() {cout<< ” Hello CObject/n ” ;} }; 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 format { UINT nMessage; //windows message UINT nCode; //control code or WM_NOTIFY code UINT nID; //control ID(or o for windows message) UINT nLastID; //used for entries specifying a range og control id ’ s UINT nSig; //signature type (action) or pointer to message AFX_PMSG pfn; /runtime to call ( or special value) } class CwinThread : public CCmdTarget { DECLARE_DYNAMIC(CWinThread) public: CwinThread:: CwinThread () { } CwinThread::~ CwinThread () { } Virtual BOOL InitInstance(){ Return TRUE; } virtual int Run(){ return 1; } }; class CWnd; class CWinApp : public CwinThread { DECLARE_DYNAMIC(CWinApp) public: CWinApp * m_pCurrentWinApp; CWnd * m_pMainWnd; Public: CWinApp::CWinApp() { pCurrentWinApp = this ;} CWinApp::~CWinApp() { } virtual BOOl InitApplication () { Return TRUE; } virtual BOOL InitInstance () { return TRUE; } virtual int Run () { 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 { DECLARE_DYNCREATE(CFrameWnd) public: CFrameWnd:: CFrameWnd () { } CFrameWnd::~CFrameWnd () { } BOOL Create (); Virtual BOOL PreCreateWindow(); DECLARE_MESSAGE_MAP() }; Class CView : public CWnd { DECLARE_DYNAMIC(CView) public: CView:: CView () { } CView::~ CView () { } DECLARE_MESSAGE_MAP() }; //global function CWinApp *AfxGetApp(); 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), MFC.CPP #include “ my.h ” //本该包含mfc.h,但为了CmyWinapp的定义 extern CmyWinApp theApp ; //external global object BOOL CWnd::Create() { return TRUE; } BOOL CWnd::CreateEx() { PreCreateWindow(); return TRUE; } BOOL CWnd::PreCreateWindow() { return TRUE; } BOOL CframeWnd::Create() { CreateEx(); return TRUE; } BOOL CframeWnd::PreCreateWindow() { return TRUE; } //global function CWinApp * AfxApp() { return theApp.m_pCurrentWinApp; } AFX_MSGMAP * CCmdTarget::GetMessageMap() const { return &CCmdTarget::messageMap; } AFX_MSGMAP CCmdTarget::messageMap = { NULL; &CCmdTarget::_messageEntries[0] }; AFX_MSGMAP_ENTRY CCmdTarget::messageEntries = { (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() MY.H #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() }; MFC.CPP #include “ my.h ” CmyWinApp theApp; //global object BOOL CMyWinAPP::InitInstance() { 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 ” , CDocument , “ CDocument ” , CMyDocid , “ CMyDoc ” , }; 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(;pMessage!=NULL;pMessageMap = PmessageMap->pBasemessageMap) { AFX_MSG_ENTRY * lpEntry= pMessageMap->lpentried; PrintflpEntries(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_ppMainWnd; 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<< ” C pMyFrameWnd Message Map: ” <<endl; MsgMapPrinting(pMessageMap); pMessageMap = pApp->GetMessageMap(); Cout<<endl<< ” CMyWinApp Message Map: ” <<endl; MsgMapPrinting(pMessageMap); }