当前位置:C++技术网 > 资讯 > MFC消息路由机制详细介绍

MFC消息路由机制详细介绍

更新时间:2015-06-07 19:30:45浏览次数:1+次

MFC消息路由机制的生成检测和执行过程如下。

1.类向导生成

    消息处理分为三个部分,第一个是消息映射,第二个是处理函数声明,第三个是处理函数定义。类向导在生成消息映射处理时做的就是这三个工作。

    当双击一个窗口模板(编辑窗口时的窗口叫做窗口模板,运行时看到的才叫做窗口),类向导检测消息映射窗口有没有对应消息映射声明,如果没有则提示为窗口添加处理类。此类就默认自动创建好后就添加好了,消息映射声明和消息映射框架,以及相关的额数据交换声明和数据控件验证声明。因为每个窗口都需要窗口函数进行消息处理,所以消息映射声明就已经将窗口的内部消息处理机制进行了声明和包装。留下消息映射对作为消息处理的接口。因此,我们只需要在这个接口中就可以实现消息处理。当然,类和窗口模板是分开的,要将窗口和类进行关联,就好比API中通过窗口函数回调函数的函数指针进行关联。MFC中通过enum { IDD = 窗口ID };语句将类与窗口进行关联,内部完成的操作就是回调函数的指定。而每个消息的封装,都是封装到了消息处理函数中,也就是在消息映射对里面提供的接口。还可能提供构造函数和析构函数的声明和空定义。空白窗口模板启动类向导就做这些事情。

    当非空白的窗口模板双击了控件,则类向导根据控件的ID和内部定义的操作类型确定窗口的事件消息和消息类型,然后提示输入处理函数名,然后创建一个消息映射,此间的工作就是消息处理的三步。

2.编译检测

    编译时,检测实现文件的消息映射,然后根据消息映射检查头文件是否有消息处理函数的声明,没有声明则提示“未声明的标识符”,如果有声明但是没有实现函数,则编译时找不到函数提示“无法解析的外部符号”。

    如果正确,继续下面两步内部的操作,第一就是将此消息的相关数据如消息类型和控件ID等填入消息结构体数组,然后将消息映射结构体进行填充。消息结构体的第一个是基类指针,第二个是消息结构体数组的地址。每一个消息处理类有且只有一个消息映射结构体数组即消息映射表和一个消息映射结构体。

3.运行路由机制

    程序运行时,只有一个窗口能够接受到用户操作消息。此时操作的消息会由系统自动识别,然后交给响应的窗口函数进行处理。后台的消息需要程序员指定消息的数据以及处理函数,这个系统无法自动识别,只能事先指定。而用户操作而产生的消息,消息参数无法事先指定,如单击的坐标只有操作时才确定,因此只有等运行时系统自动检测和分派消息。(根据这个原理,可以模拟出人工单击消息,从而实现机器自动批量操作。)因为用户操作产生的消息只有一个窗口能接受到,也就是活动窗口,而活动窗口只有一个,因此,只有一个窗口函数才能处理此消息。

    系统捕获到操作消息,将消息参数填充好,将消息投递到活动窗口的消息队列,等待处理。而具体的消息处理,比如单击,可能单击在控件上,可能单击在主窗口之上,或者标题栏上,这就要进一步进行判断。消息都进入消息队列之前,消息要进行处理才能被窗口函数处理。比如说单击,单击到哪个控件上,直接的消息只有得到活动窗口和单击的屏幕坐标,这样的消息到窗口函数中无法处理,需要转换成具体的信息。系统计算后得到单击到的坐标和窗口的各个控件的区域进行比较测试,如果在按钮上,说明单击了按钮。然后根据事先写好的资源模板文件找到改按钮获得按钮的ID,然后将单击操作转换成LBUTTONDOWN标识符和将按钮ID读取出来,填充到消息结构体中,然后消息就分派到窗口函数。如果是标准消息就直接进行处理,标准消息也就是单击到窗口本身的区域的单击这一类消息,而单击到菜单和控件上的在读取窗口资源文件(编译后的)时根据文件中的窗口类型而不是ID进行判断是窗口还是控件还是菜单,如果是窗口类型的就是标准消息,如果是控件或者菜单的就是非标准消息,则进行下一步分析处理。
    而这一部分的消息处理就是MFC消息路由机制。消息到这里,已经附带了足够的信息。MFC将消息拿到此窗口处理类的消息映射表进行比对查找(根据消息的ID进行查找),如果找到,则取出函数指针的值查找函数,然后执行函数。到此消息就处理完了。如果查找时没有找到对应的ID消息,则返回未找到,然后就取出消息映射结构体的父类指针,找到父类,然后将此消息传递到父类中,继续同样的方式查找,如果没找到则继续往父类的父类进行查找,如果一致到最开始的类就是始祖类,也没有这个消息,则查找结束,最后将消息投递到默认处理函数中处理,最后处理完毕返回。
    所以在判断消息处理时,只要判断窗口的层次即可,哪个窗口在哪个窗口之上,最上面的就接到消息,处理时就往其处理类的消息映射的父类指针传递,所以在此时,就可以改变消息路由的顺序,可以向任何方向流动,而MFC的非标准消息的流动复杂就是考虑到了兄弟类的情况,而不是简单的往父类传递,而改变了形成从而变得很绕,顾及到了所有的类。而这些类很多,自然路由就非常的复杂。