MFC消息映射机制(2)

来源:互联网 发布:手机实用小五金软件 编辑:程序博客网 时间:2024/06/06 06:52

说起MFC的消息映射机制,刚开始学的时候可能觉得很神奇,怎么通过3个宏就可以实现整个程序的消息循环。其实你将宏展开便会有所发现,这些宏就是在类里面定义了一些成员变量和成员函数,然后通过类外的宏将其实现。然后通过面向对象的相关性质,在库函数中进行实现。下面我们分开来说明下。

先来认识两个重要的数据结构:

Struct AFX_MSGMAP_ENTRY{

UINT nMessage;    //消息ID

UINT nCode;         //通知码

UINT nID;            //命令ID/控件ID

UINT nLastID;      //最后一个控件ID

UINT nSig;            //消息处理函数类型

AFX_PMSG pfn;   //消息处理函数的地址(指针)

};

 

Struct AFX_MSGMAP{

CONST AFX_MSGMAP *pBaseMap;

CONST AFX_MSGMAP_ENTRY* lpEntries;

};

 

消息映射的实现这两个数据结构起到了相当重要的作用。首先类里面定义了一个静态AFX_MSGMAP类型的成员变量,通过上面我们可以看出这个结构中有两个成员,一个是相同类型的变量地址,一个是AFX_MSGMAP_ENTRY类型的地址。然而通过类外的实现宏赋值。我们可以清楚的看到,前者是父类的静态变量的地址。后者是本类静态数组的地址。而静态数组的每一项便是上面的结构体一,{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }这是这个结构的结尾,于是我们可以猜测,在MFC库中肯定进行了某种搜索,以上面写出的那项作为结束标志。

学过C语言的链表应该都是很清楚的,通过自己的next域找到下一个结点。这个的AFX_MSGMAP中有保存父类同类型的地址,会不会其实也像链表一样,可以让我们一直遍历这条继承链呢,其实MFC就是这样做的。那么怎么获取链表的头节点呢,我们的宏展开后还有一个函数成员函数GetMessageMap,就是这个函数返回了我们类中的静态成员变量AFX_MSGMAP对象的地址。到这里我们便得到了这个链的头结点。最后说明一点,你在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏之间添加的消息宏便是添加了lpEntries这个数组中的项。

现在我们来看看消息处理的过程:

当一个消息产生的时候...

1、首先利用GetMessageMap获取本类静态变量的地址(链表头结点)pMessageMap。

2、利用pMessageMap的第二个参数获取本类静态数组的地址,然后在数组中查找消息对应的处理函数,如果找到了执行3,没找到执行4.

3、使用找到的数组元素的最后一个成员(成员函数的地址),并且调用这个函数完成消息的处理。(如果自己类中没有实现这个函数,便会调用父类同名函数)

4、利用pMessageMap的第一个元素获取父类的静态变量地址,并重新给pMessageMap赋值为父类的静态变量地址执行2

5、如果遍历整个链表还是没有找到,调用DefWndProc对消息做默认处理!

 

最后值得一提的是MFC的消息分类:

1.window的标准消息 ON_WM_XXX   例如:ON_WM_CREATE()

2.命令消息(WM_COMMAND)(也是Window标准消息因为比较特殊分开来写)

i.          ON_COMMAND(命令ID,处理函数)

ii.        ON_COMMAND_RANGE(起始ID,终止ID,处理函数)

这里的处理函数需要自己地址,类型可以再MSDN中查询

3自定义消息

#define    WM_MYMESSAGE      WM_USER+n;

添加宏ON_MESSAGE(消息ID,处理函数)

注意这里的WM_USER是一个宏,表示自定义消息的起点

4通知消息(ON_通知码) 例如: ON_EN_CHANGE

0 0