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();
//mfc.cpp
#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()
//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()
};
//my.cpp
#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 },