MFC的消息映射机制其实还是挺简单的, 下面用一个小的程序来演一下其工作原理.
/*----------------------------------------------------------------------------
MFC 消息映射机制的演示程序
作者:Ning 2014/02/28
备注:
审核:
----------------------------------------------------------------------------*/
typedef int MessageID;
#define afx_msg
/*===========================================================================
功能: 定义消息映射结构体 2014/02/28
===========================================================================*/
class CmdTarget;
typedef void (CmdTarget::*MemberFunc)();
// 定义一个消息结构, 关联消息ID与消息响应函数
struct MsgMap
{
MessageID m_Msg;
MemberFunc m_pFunc;
};
typedef MsgMap* MsgMapPtr;
struct MessageChain
{
const MessageChain* m_pParent;
MsgMapPtr m_MessageEntries;
};
/*===========================================================================
功能: 消息映射基类 2014/02/28
===========================================================================*/
class CmdTarget
{
public:
void MessageHandler(MessageID A_Msg);
protected:
virtual const MessageChain* GetMsgChain() = 0;
private:
static const MessageChain m_MsgChain;
};//end CmdTarget
const MessageChain CmdTarget::m_MsgChain = {NULL, NULL};
/*----------------------------------------------------------------------------
2014/02/28 功能: 在消息映射数组中搜索与ID对应的消息处理函数并执行
----------------------------------------------------------------------------*/
void CmdTarget::MessageHandler(MessageID A_Msg)
{
// 从当前窗口中获取消息映射数组, 查找是否有对应的消息处理函数
const MessageChain* pMsgChain = GetMsgChain();
while(pMsgChain != NULL)
{
MsgMapPtr pEntries = pMsgChain->m_MessageEntries;
if(pEntries != NULL)
{
for(int i = 0; ; i++)
{
if(pEntries[i].m_Msg == 0)
break;
if(pEntries[i].m_Msg == A_Msg)
{
// 调用消息处理函数(这句稍稍有点复杂)
(this->*pEntries[i].m_pFunc)();
return;
}
}
}
// 子类没有消息函数, 再从父类中查找
pMsgChain = pMsgChain->m_pParent;
}
}
/*----------------------------------------------------------------------------
2014/02/28 功能: 类中声明消息映射相关的变量与函数
----------------------------------------------------------------------------*/
#define MY_DECLARE_MESSAGE_MAP() \
private:\
static MsgMap m_MsgMap[];\
static const MessageChain m_MsgChain;\
protected:\
virtual const MessageChain* GetMsgChain();
/*----------------------------------------------------------------------------
2014/02/28 功能: 开始建立消息映射数组
----------------------------------------------------------------------------*/
#define MY_BEGIN_MESSAGE_MAP(derived_class, base_class) \
const MessageChain MyWindow::m_MsgChain = {&CmdTarget::m_MsgChain, \
&MyWindow::m_MsgMap[0]}; \
const MessageChain* MyWindow::GetMsgChain() \
{return &MyWindow::m_MsgChain;} \
MsgMap MyWindow::m_MsgMap[] = {
/*----------------------------------------------------------------------------
2014/02/28 功能: 结束消息映射数组
从C++指针中不能获取数组的尺寸,所以把最近一个元素定义成0,就可以明确定位数组结束的位置.
----------------------------------------------------------------------------*/
#define MY_END_MESSAGE_MAP() \
{0, NULL} \
};
/*----------------------------------------------------------------------------
2014/02/28 功能: 添加数组元素的宏
----------------------------------------------------------------------------*/
#define MY_ON_CONTROL(id, func) {id, (MemberFunc)&func},
/*===========================================================================
功能: 模拟一个用户定义的窗口 2014/02/28
===========================================================================*/
#define ID_PAINT 0x00000001
#define ID_BUTTON 0xFFFF0001
class MyWindow : public CmdTarget
{
protected:
//{{AFX_MSG(Derived)
afx_msg void OnButtonDown();
afx_msg void OnPaint();
//}}AFX_MSG
MY_DECLARE_MESSAGE_MAP() // 声明消息映射相关的成员
};
MY_BEGIN_MESSAGE_MAP(MyWindow, CmdTarget)
//{{AFX_MY_BEGIN_MESSAGE_MAP
MY_ON_CONTROL(ID_BUTTON, OnButtonDown) // 成员函数与控件绑定
MY_ON_CONTROL(ID_PAINT, OnPaint) // 成员函数与窗口消息绑定
//}}AFX
MY_END_MESSAGE_MAP()
void MyWindow::OnButtonDown()
{
printf("ButtonDown\n");
}
void MyWindow::OnPaint()
{
printf("Paint\n");
}
/*===========================================================================
功能: 主函数 2014/02/28
===========================================================================*/
int main(int argc, char* argv[])
{
MyWindow oDerived;
// 模拟由系统消息泵向窗口发送消息
oDerived.MessageHandler(ID_BUTTON);
oDerived.MessageHandler(ID_PAINT);
return 0;
}