为了支持消息映射,MFC提供了3种宏。
消息映射的声明和分界宏包含在CCmdTarget类中,如下表:
就是这3个宏组织了一张庞大的消息映射网络。所有继承与CCmdTarget类的派生类均具有这种特性。下面分别说说这个宏
这个宏的定义在AfxWin.h文件中定义,源码如下:
#define DECLARE_MESSAGE_MAP() \
protected: \
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \
#define BEGIN_TEMPLATE_MESSAGE_MAP(theClass, type_name, baseClass) \
PTM_WARNING_DISABLE \
template < typename type_name > \
const AFX_MSGMAP* theClass< type_name >::GetMessageMap() const \
{ return GetThisMessageMap(); } \
template < typename type_name > \
const AFX_MSGMAP* PASCAL theClass< type_name >::GetThisMessageMap() \
{ \
typedef theClass< type_name > ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
static const AFX_MSGMAP messageMap = \
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return &messageMap; \
} \
PTM_WARNING_RESTORE
可以看出,DECLARE_MESSAGE_MAP宏主要定义了一个长度不定的静态数组变量_messageEntries[]、一个静态变量messageMap和一个虚拟函数GetMessageMap。静态数组_messageEntries[]定义了一张消息映射表,表中每一项指定了类或对象的消息和处理此消息的函数之间的对应关系,代表一条映射,可以用AFX_MSGMAP_ENTRY结构描述。
我们看到了一个陌生的类型 AFX_MSGMAP ,查看其定义:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
这个结构体第一个成员是一个函数指针,第二个成员类型为 AFX_MSGMAP_ENTRY* ,查看AFX_MSGMAP_ENTRY 定义:
struct AFX_MSGMAP_ENTRY
{
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_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
从上述结构可以看出,每条映射有两部分内容:前4个成员是关于消息ID的,后两个成员是关于消息对应的执行函数的。其中,nSig成员用来标志不同原型的消息函数,MFC根据nSig的不同把消息派发给对应的函数进行处理。而pfn成员则是一个指向CCmdTarget成员函数的指针,函数指针的类型定义如下:typedef void (AFX_MSG_CALL CCmdTarget::AFX_PMSG)(void)
当初始化消息映射表时,各种类型的消息函数都被转换成相同的类型,而实际执行时,根据nSig把pfn还原成相应类型的消息处理函数,并执行它。
另一个AFX_MSGMAP类型成员变量messageMap,是一个包含消息映射的变量,把消息映射的信息和相关函数打包在一起,其结构定义如下:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();//指向_GetBaseMessageMap函数
const AFX_MSGMAP_ENTRY* lpEntries;//保存基类消息映射入口_messageEntries的地址。
};
可见,AFX_MSGMAP实际上定义了一单项链表,链表中的每一项是一个指向消息映射表的指针。其主要作用是获取基类和本身的消息映射入口地址。于是我们知道MFC首先把所有消息一条条填入到AFX_MSGMAP_ENTRY结构中去,形成一个数组,该数组存放了所有消息和他们相关的参数,在通过AFX_MSGMAP获得该数组的首地址及基类的消息映射入口地址。当本身对该消息不响应的时候,就调用其基类的消息响应,因此,基类的消息处理函数就是派生类的默认消息处理函数。
这两个宏的定义也在AfxWin.h文件中,代码如下:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) /
const AFX_MSGMAP* theClass::GetMessageMap() const /
{ return &theClass::messageMap; } /
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = /
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; /
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = /
{ /
实际应用BEGIN_MESSAGE_MAP(myview,CView)等价于
const AFX_MSGMAP* myview::GetMessageMap() const /
{ return & myview::messageMap; } /
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP myview::messageMap = /
{ & CView::messageMap, & myview::_messageEntries[0] }; /
AFX_COMDAT const AFX_MSGMAP_ENTRY myview::_messageEntries[] = /
{ /
END_MESSAGE_MAP和BEGIN_MESSAGE_MAP是成对出现的
#define END_MESSAGE_MAP() /
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } /
}; /
可以看出,DECLARE_MESSAGE_MAP宏在其类中申请了一个全局结构和获得该结构的函数,而在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间填写刚才的全局结构,将消息和对应的函数联系起来,并通过AFX_MSGMAP中的pBaseMap指针,将各类按继承顺序连接起来,从而提供消息流动的道路。
除了上面的3个宏外,常用的宏还有:
最常用的ON_COMMAND宏定义如下:
#define ON_CONMMAND(id,memberFxn)
{
WM_COMMAND,CN_COMMAND,(WORD)id,(WORD)id,AfxSig_vv,
(AFX_PMSG)&OnFileNew}
根据上面的定义,ON_COMMAND(ID_FILE_NEW,OnFileNew),将被预编译器展开为如下代码:
{WM_COMMAND,CN_COMMAND,(word)ID_FILE_NEW,(WORD)ID_FILE_NEW,AfxSig_vv,(AFX_PMSG)&OnFileNew},
虽然消息映射宏很重要,但是通常不需要用户直接使用它们。当使用ClassWizzard把消息处理函数与消息关联在一起的时候,他会将源文件中自动创建消息映射入口,所以就算读者没有理解上面的东西,会使用ClassWizard消息就可以。
大部分消息流动是在用户与应用程序之间进行的,一个消息一般针对一类类型对象。MFC中CWinApp类的Run函数负责把消息从应用程序的消息队列中取出,发送到应用程序窗口函数WinProc中。该函数根据消息的类别,再传送到响应的对象中。
任何派生于CCmdTarget类的对象都能接受命令消息。这些类对象组成一个有序链表,链表中的每一个
对象都可以同时接收到命令消息,命令消息按照一定的路径传送。链表中各个对象处理消息的优先级和顺序并不相同。下面以实例分析一下:
建一个基于MFC的单文档应用程序,运行后,点击帮助菜单:
会弹出一个对话框,我们简略分析一下消息映射,首先将视图切换到ClassView,双击MFC_DISCOVER.h文件,有如下代码:
class CMFC_DISCOVERApp : public CWinApp
{
public:
CMFC_DISCOVERApp();
// 重写
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
// 实现
afx_msg void OnAppAbout(); //关于对话框
DECLARE_MESSAGE_MAP() //消息宏映射申明
};
extern CMFC_DISCOVERApp theApp;
宏DECLARE_MESSAGE_MAP声明了消息映射,并初始化消息映射表。
再打开MFC_DISCOVER类的实现文件MFC_DISCOVER.cpp,观察下面的代码:
BEGIN_MESSAGE_MAP(CMFC_DISCOVERApp, CWinApp)
ON_COMMAND(ID_APP_ABOUT, &CMFC_DISCOVERApp::OnAppAbout)
// 基于文件的标准文档命令
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
// 标准打印设置命令
ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
``
当windows接收到ID_APP_ABOUT消息时,通过查找消息映射表找到相应的处理函数OnAppAbout来相应消息:
// 用于运行对话框的应用程序命令
void CMFC_DISCOVERApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}
`
ON_COMMAND(ID_APP_ABOUT, &CMFC_DISCOVERApp::OnAppAbout)`注释,则无法出现关于对话框了。
你若将