了解如何使用 windows API 函数编程后,对 windows 应用程序的大体结构框架有初步的认识。但是直接使用 windows API 进行编程程序员需要自己编写大量代码。像我这种从一开始接触程序开发就是学习的 OO 的人来说,实在是无法忍受。从现在开始,我把重点转到 VC++ 的另一方面,利用 MFC 进行和向导来编写 windows 程序。
MFC ( Microsoft Foundation Class ) 是用来编写 windows 应用程序的 C++ 类集,它封装了大量的 windows API 函数和 windows 控件,并且以层次结构组织起来。使程序员在开发时能方便的进行代码重用,加快开发速度并提高代码的可靠性。
应该注意的是 MFC 是 使用C++语言实现的一个类库,但并非是进行GUI程序设计的唯一标准。
有 OO 基础或者是学习过 JAVA 语言理解 MFC 的体系结构会比较轻松。目前, MFC 中包含了大约 100 多个类,通过定义类对象并调用对象的成员函数来实现相应的功能。
下面简要介绍一下 MFC 的类组织结构:
1. 根类 CObject
MFC 中绝大多数类和自定义子类的根类,它提供了许多编程所需要的公共操作如:对象建立和删除、串行化支持、对象诊断、运行时信息以及集合类的兼容等
2. 命令相关类 CCmdTarget
它是 CObject 的直接子类,它是 MFC 中具有消息映射属性的基类。一般很少需要程序员直接从 CcmdTarget 直接派生新类。
3. 线程基类 CWinThread
所有线程的基类,可以直接使用,它封装了操作系统的线程化功能。
4. 窗口应用程序类 CWinApp
每个应用程序有且只有一个应用程序对象,在运行程序中该对象与其它对象相互协调,该对象从 CwinApp 类中派生而来。它封装了初始化,运行,终止应用程序的代码。
5. 文档 / 视类 CDocument/CView
文档对象由文档模板对象创建,管理应用程序的数据。 CDocument 支持标准的文档操作,这些操作包括文档创建、下载、保存等。一个应用程序可以操作多种文档类型,每一种文档类型都有特定的文档模板( document template )。
用户通过文档和相关联的视图对象( CView object )与文档进行交互。一个视图显示文档中的信息,并把用户在框架窗口内的操作转换成对文档操作的相应命令。当用户打开一个文档时,应用程序实际上创建了一个视图并把这个视图和相应的文档联系在一起。一个视图充当了沟通用户和文档对象的中间桥梁的作用。
6. 可视对象类 CWnd
该类提供了 MFC 中所有窗口类的基本功能,它的消息映射机制隐藏了窗口函数 WndProc 。一个 windows 消息通过消息映射发送到相应的 CWnd 类的 OnMessage 成员函数,通过重载 OnMessage 函数来对特定的消息进行处理。
CWnd 类有许多经常使用的子类,如 CMenu 类, CDialog 类, CButton 类等等。
7. 通用类
通用类提供了许多共用服务,例如文件 I/O ( CFile 类和 CArchive 类)、异常处理( CException 类)等。
8. OLE 类
OLE1.0 规范是 MS 在 1991 年发布的。它是处理复合文档的一种方法: Object Linking and Embedding 技术。所谓复合文档,就是在一个文档中同时保存了如文本、图像和声音等多种不同类型的数据,而这些数据又可以通过不同的应用程序用不同的格式产生。
1993 年发布的 OLE2.0 是基本对象的一整套体系结构,能够扩展、定制和增强服务功能,它的理论基础是 COM 技术。
此外, ActiveX 也是一种对 OLE 技术的扩展,它使 OLE 进入 Internet 和 Intranet 。
9. ODBC 类
为了支持带有 ODBC 驱动程序的各种数据库管理系统, MFC 也提供了 Cdatabase 和 CRecordset 类,用于管理数据库的连接及其操作。
10. MFC 中全局函数和变量
在 MFC 中提供的全局函数和变量一般都以 Afx 开头,它们可以在 MFC 应用程序的任务地方中使用,如: AfxAbort 、 AfxMessageBox 等等。
在使用 VC 生成 MFC 应用程序时, IDE 会自动生成许多代码,以一个简单的对话框程序为例,将会有如下几个主要的文件:
stdafx.h/cpp 标准系统包含文件,或是常使用但不常更改的
MFCles1.h/.cpp 应用程序类
MFCles1Dlg.h/cpp 对话框类
Resource.h 资源头文件,被 MFCles1.rc 所使用
MFC 应用程序的一般结构
就设计模式来看, MFC 应用程序是一个标准的单例模式。每一个应用程序有且仅有一个实例对象: theApp
程序执行的基本流程,就我现在的理解而言:
首先应该从在应用程序体系结构类中声明的一个全局实例对象 theApp 入手(C++里的全局类对象初始化在main函数之前),在进入CWinApp类的构造函数之后,程序才进入原来Windows API里的那个入口函数:AfxWinMain。 我们在写MFC程序时并没有显示的指出WinMain入口函数的位置。这个函数的定义被安置在了APPMODUL.CPP文件里(这一文件位于VS安装目录下的VC98目录里的MFC源文件里)。每个app实例被构造后,会产调用相应的InitInstance函数进行初始化。原来的注册窗口类的工作被放在了WINCORE.CPP文件里的AfxEndDeferRegisterClass函数,这里MFC已经为我们定义好了几种默认的窗口类。然后就会调用CPreCreateWnd函数进行窗口的显示工作。最后,使用CWinThread::Run方法启动消息循环,至此,整个窗口应用程序就运行起来了。windows应用程序可以被同时多次运行,第一次运行完成初始化工作后,以后每次运行程序拷贝都全调用InitInstance函数进行初始化工作。
class CMFCles1App : public CWinApp
{
public:
CMFCles1App();
// 重写一些父类方法
public:
virtual BOOL InitInstance();
// 实现消息映射的宏
DECLARE_MESSAGE_MAP()
};
extern CMFCles1App theApp;// 代码应用程序本身
以一个简单的对话框程序为例,在InitInstance函数里完成了以下工作
CMFCles1Dlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
第一行声明了一个对话框类对象,第二行将其赋值给当前应用程序类的成员变量,这是一个指向主窗口的指针,最后一行显示窗口。
了解了一点关于应用程序类的知识后,再来分析剩下的与GUI有关的另一个类,这里使用一个对话框类作一点说明。
同样, MFCles1Dlg 则派生自 CDialog 类
class CMFCles1Dlg : public CDialog
{
public:
CMFCles1Dlg(CWnd* pParent = NULL); // 标准构造函数
protected:
HICON m_hIcon;// 应用程序图标的句柄
DECLARE_MESSAGE_MAP()
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
};
DECLARE_MESSAGE_MAP() 是一个用来定义消息映射的宏,而下面以 afx_msg 开头的函数都是消息的响应函数,这比起 API 里面把消息都写在一些 switch 语句中要更清楚,也方便了修改。要实现对话框对消息的响应还需要在类中加上这两行
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
所有的消息映射函数都要写在这两行之间,例如ON_WM_RBUTTONDOWN( )就定义了一个右键按下的消息映射。MFC已经有了预定义好了许多消息映射函数的格式,需要查看可以参考MSDN。