首页
社区
课程
招聘
[原创]如何在MFC中定位按钮事件
发表于: 2019-1-4 10:47 9901

[原创]如何在MFC中定位按钮事件

2019-1-4 10:47
9901

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这个就是虚函数表的指针。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 6
支持
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  junkboy   +1.00 2019/01/04
最新回复 (12)
雪    币: 1430
活跃值: (4383)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
2
我正好需要这个
2019-1-4 11:03
0
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持
2019-1-4 12:59
0
雪    币: 1808
活跃值: (578)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
4
各个版本的MFC在进入afxWinMain之前做了不同的事情,关键点在于寻找WinMain入口函数,进入AfxWinMain寻找应用程序指针;

2019-1-4 13:21
0
雪    币: 2058
活跃值: (2521)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很详细的  支持一下
2019-1-4 15:50
0
雪    币: 66
活跃值: (2735)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
MFC高版本区别也不大 ,通过这篇文章完全可以很快的摸通
2019-1-8 15:01
0
雪    币: 1808
活跃值: (578)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
7
区别不大,但是对于找到afxwinmai有很大的帮助
2019-1-8 15:57
0
雪    币: 515
活跃值: (3257)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wem
8
好文
2019-1-13 02:37
0
雪    币: 3676
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
mfc是什么?
2019-1-13 22:05
0
雪    币: 1808
活跃值: (578)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
10
微软基础类库,用于快速图形界面开发
2019-1-16 13:36
0
雪    币: 1095
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
怎么找到窗口的虚函数,说的有点含糊,不太懂
2019-1-17 10:33
0
雪    币: 1808
活跃值: (578)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
12
带有虚函数的类,从this指针地址处出去4个字节的内容,这4个字节指向的就是虚函数表
2019-1-21 09:59
0
雪    币: 1
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
你好,你提供的例子程序是静态编译的吗,没有导入mfc的dll?
2019-11-19 17:59
0
游客
登录 | 注册 方可回帖
返回
//