之前已经介绍了Windows程序的设计过程及关键,简单一句话就是把界面设计好,关键是做好交互操作的处理过程。
Window程序设计的大框架基本固定:设计窗口类,注册,创建,显示,更新,消息循环。在这个过程中,设计、注册、创建、显示和更新窗体,包括消息循环过程等等所有操作都是调用API函数来完成的,所谓的API函数就是操作系统提供给应用程序的编程接口。
除了提供API函数库供编程使用,针对面向对象编程理念,微软还对这些API函数进行了封装,将其根据功能划分到各种C++类中。程序员通过调用类的成员函数来调用这些API函数。
MFC就是微软为了简化程序员的开发工作所开发的一套C++类的集合。除了将函数封装到类,MFC还将每一个窗口应用程序都需要的步骤进行了封装,即将Window程序设计的大框架进行了封装。所以简单来说,MFC就是对API函数库和Windows程序设计过程进行了封装,目的是简化程序员的开发工作,但是对初学者却造成了很多不必要地困扰,在没有了解MFC的架构前看代码无从下手,找不到入口点。
下面以通过MFC AppWizard生成的单文档MFC应用程序为例来进行讲解,工程名为Test。自动生成的工程有五个类:CAboutDlg,CMainFrame,CTestApp,CTestDoc,CTestView,和一个CTestApp类型的全局对象theApp。
Windows应用程序的消息处理过程中,操作系统是通过应用程序实例句柄来找到消息所属的应用程序,再通过窗口句柄进一步找到消息所属的窗口,进而调用其窗口过程函数。对于MFC程序而言,通过产生一个应用程序类CTestApp的对象theApp来唯一标识应用程序的实例。每个MFC程序有且仅有一个应用程序类(CWinApp)的派生类(CTestApp)。每个MFC程序实例有且仅有一个该派生类的实例化对象,即theApp全局对象。
全局变量是在main函数之前分配内存空间,所以在进入主函数之前,首先是创建CTestApp的对象。而CTestApp是CWinApp的子类,所以在创建该类的实例前,首先调用了CWinApp的构造函数。该函数完成程序运行时的一些初始化工作。
接着进入主函数。主函数中即完成窗体的设计、注册、创建、显示、更新等等一系列过程,并启动消息循环过程。具体表现就是调用theApp的InitApplication、InitInstance和Run函数。InitApplication函数完成MFC内部管理方面的工作。由CTestApp的父类CWinApp完成。InitInstance函数因为CTestApp重写之,所以调用的是CTestApp类内的函数实现。这两个函数完成了窗体的设计直到更新显示。而Run函数则启动了消息循环过程。
至此,MFC的大框架介绍完毕。
Windows程序设计的关键是窗口过程函数的设计。一般的Windows应用程序在窗口过程函数中使用switch对消息进行判别并分类处理。而MFC的消息循环并不是采用Window应用程序所采用的方式,它采用专门的消息映射机制。
至于原因,个人总结是:在介绍一般Windows程序设计时,每个窗口对象对应一个窗口过程函数,该窗口的所有消息均由该函数进行处理;而MFC架构中采用了文档/视图结构,该架构由五个类共同实现:CAboutDlg,CMainFrame,CTestApp,CTestDoc,CTestView。该应用程序可能接收到的消息也被划分到不同的类中,并且该架构中类的继承层次较复杂,如果将类可能接收到的消息作为类的成员变量存在,类的体积将非常庞大,所以采用消息映射机制来实现。
MFC消息映射机制的具体实现是:
在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只能搜索该消息静态表,查看表中是否含有该消息,就可知道该类能够处理此消息。如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。