1,MFC消息映射机制
MFC消息映射机制有关的宏有下面几个: DECLARE_MESSAGE_MAP()宏 BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏 弄懂MFC消息映射机制的最好办法是将找出一个具体的实例,将这些宏展开,并找出相关的数据结构。 DECLARE_MESSAGE_MAP()
DECLARE_MESSAGE_MAP()宏的定义如下: #define DECLARE_MESSAGE_MAP() / private: / static const AFX_MSGMAP_ENTRY _messageEntries[]; / protected: / static AFX_DATA const AFX_MSGMAP messageMap; / virtual const AFX_MSGMAP* GetMessageMap() const; / 从上面的定义可以看出,DECLARE_MESSAGE_MAP()作下面三件事: 定义一个长度不定的静态数组变量_messageEntries[]; 定义一个静态变量messageMap; 定义一个虚拟函数GetMessageMap(); 在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中两个对外不公开的数据结构 AFX_MSGMAP_ENTRY和AFX_MSGMAP。为了弄清楚消息映射,有必要考察一下这两个数据结构的定义。
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 nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) };
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
结构中各项的含义注释已经说明得很清楚了,这里不再多述,从上面的定义你是否看出,AFX_MSGMAP_ENTRY结构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量_messageEntries[]实际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,因而这张表也称为消息映射表。再看看AFX_MSGMAP的定义。 (2)AFX_MSGMAP的定义 struct AFX_MSGMAP { const AFX_MSGMAP* pBaseMap; const AFX_MSGMAP_ENTRY* lpEntries; }; 不难看出,AFX_MSGMAP定义了一单向链表,链表中每一项的值是一指向消息映射表的指针(实际上就是_messageEntries的值)。通过这个链表,使得在某个类中调用基类的的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数”就“顺理成章”了。在后面的“MFC窗口的消息处理”一节中会对此作详细的讲解。 BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP() 它们的定义如下: #define BEGIN_MESSAGE_MAP(theClass, baseClass) / const AFX_MSGMAP* theClass::GetMessageMap() const / { return &theClass::messageMap; } /
//DECLARE_MESSAGE_MAP()中有一个messageMap
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = / { &baseClass::messageMap, &theClass::_messageEntries[0] }; / AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = / { / #define END_MESSAGE_MAP() / {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } / }; / 对应BEGIN_MESSAGE_MAP()的定义可能不是一下子就看得明白,不过不要紧,举一例子就很清楚了。对于BEGIN_MESSAGE_MAP(CView, CWnd),VC预编译器将其展开成下面的形式: const AFX_MSGMAP* CView::GetMessageMap() const { return &CView::messageMap; } AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap = { &CWnd::messageMap, 父类的MessageMap &CView::_messageEntries[0] }; AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] = { 至于END_MESSAGE_MAP()则不过定义了一个表示映射表结束的标志项,我想大家对于这种简单的技巧应该是很熟悉的,无需多述。 到此为止,我想大家也已经想到了象ON_COMMAND这样的宏的具体作用了,不错它们只不过定义了一种类型的消息映射项,看看ON_COMMAND的定义:
//第二个参数为函数地址 消息映射 #define ON_COMMAND(id, memberFxn) / 绑定消息与函数 { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn }, 根据上面的定义,ON_COMMAND(ID_FILE_NEW, OnFileNew)将被VC预编译器展开 如下: {WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&OnFileNew},
BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP() 宏中间的宏的作用就是填充
_messageEntries数组。
2,原理
GetMessageMap是虚函数,存在于虚函数表里,只要找到全局应用程序对象指针就可以了。
下图为一个MFC程序的入口,程序会先进入_tWinMain,然后再进入AfxWinMain。
继续跟进可以看到调用AfxGetApp就可以得到应用程序指针。
3,OD分析
用VC6.0建立一个如图的mfc程序,并设置2个按钮事件,和相应的处的事件处理函数。编译后在拖入OD中。
找到getMoudleHandleA函数的后的下一个call就是_twinmain函数,F7进入函数内部。
进入函数后,里面只有一个call,这个函数就是afxWinMain,再F7进入,
找到afxGetApp函数,这个函数会返回应用程序指针,返回值放入到eax中,执行到0048c11a位置。
点击eax的值在数据窗口中跟随,里面存储了005b4064这个就是虚函数表的指针。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
上传的附件: