MessageMapping (消息映射)
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);
}