MFC类库的产生与发展
在Microsoft推出Windows 3.0之后,Windows操作系统受到越来越多的人的青睐。但是不久,开发人员发现撰写Windows应用程序是重复、厌烦的过程,而且效率极低。因为每一个窗口都需要在窗口函数中处理大量的消息,导致出现大量的重复代码。
随着C++语言的盛行,开发人员发现利用C++语言的特性来封装Windows API和Windows应用程序的开发可以明显地提高程序的开发效率。于是,人们对这种新的开发模式有了强烈的需求。在这种情况下,Microsoft公司和其他一些大的厂商开始从事Windows框架的设计。MFC就在这种背景下诞生了。
MFC的英文全称是Microsoft Fundation Class Library,即微软基础类库。从其产生至今已经发展了十几个版本,表1.4列出的是MFC的发展过程。
表1.4 MFC发展过程
MFC | Visual C++ |
MFC 1.0版本 | Microsoft C/C++ 7.0版本 |
MFC 2.0版本 | Visual C++ 1.0版本 |
MFC 2.5版本 | Visual C++ 1.5版本 |
MFC 3.0版本 | Visual C++ 2.0版本 |
MFC 3.1版本 | Visual C++ 2.1版本 |
MFC 3.2版本 | Visual C++ 2.2版本 |
MFC 4.0版本 | Visual C++ 4.0版本 |
MFC 4.1版本 | Visual C++ 4.1版本 |
MFC 4.2版本 | Visual C++ 4.2版本 |
MFC 4.21版本 | Visual C++ 5.0版本 |
MFC 6.0版本 | Visual C++ 6.0版本 |
MFC 7.0版本 | Visual C++.NET 2002版本 |
MFC 7.1版本 | Visual C++.NET 2003版本 |
MFC 8.0版本 | Visual C++.NET 2005版本 |
1.5.2 MFC类库层次
MFC中的类按照功能的不同,可以分为不同的层次。图1.10显示了MFC中的所有类及类库层次。
1.5.3 MFC常用数据类型
MFC中的数据类型与Windows SDK开发包中的数据类型多数是一致的,但也有一些数据类型是MFC独有的。表1.5列出了MFC中使用的数据类型。
表1.5中列出的最后两个数据类型是MFC所特有的。
1.5.4 MFC全局函数
在MFC类库中,除了提供实现各种功能的类以外,还提供许多全局函数。具体介绍如下。
表1.5 MFC数据类型
数 据 类 型 | 描 述 |
BOOL | 布尔值,取值为TRUE或FALSE |
BSTR | 32位字符指针 |
BYTE | 8位无符号整数 |
COLORREF | 用作颜色值的32位数值 |
DWORD | 32位无符号整数,或者段的地址和与之相关的偏移量 |
LONG | 32位有符号整数 |
LPARAM | 32位值,作为窗口函数或回调函数的参数 |
LPCSTR | 指向字符串常量的32位指针 |
LPSTR | 32位字符串指针 |
LPCTSTR | 指向兼容Unicode和DBCS字符集的字符串常量32位指针 |
LPTSTR | 指向兼容Unicode和DBCS字符集的字符串32位指针 |
LPVOID | 指向一个未定义类型的32位指针 |
LRESULT | 窗口函数或回调函数返回的32位值 |
UINT | 32位无符号整数 |
WNDPROC | 指向一个窗口函数的32位指针 |
WORD | 16位无符号整数 |
WPARAM | 作为参数传递给窗口函数或回调函数的值 |
POSITION | 用于标记集合中一个元素的位置 |
LPCRECT | 指向一个RECT结构体常量的32位指针 |
1.MFC诊断函数
为了调试的方便,MFC提供了多个诊断函数,如表1.6所示。
表1.6 MFC诊断函数
函 数 名 称 | 描 述 |
AfxCheckMemory | 检查当前分配的所有内存的完整性 |
AfxDump | 如果在调试器内调用,则转存对象的状态 |
AfxDumpStack | 生成当前栈的一个映像,该函数通常被静态链接 |
AfxEnableMemoryTracking | 打开或关闭内存跟踪 |
AfxIsMemoryBlock | 检验一个内存块是否被正确地分配 |
AfxIsValidString | 检验一个字符串指针是否有效 |
AfxSetAllocHook | 允许在每次进行内存分配时调用一个函数 |
AfxDoForAllClasses | 对所有从CObject继承的支持运行时检查的类执行一个特定的功能 |
AfxDoForAllObjects | 对所有从CObject继承的用new分配内存的对象执行一个指定的功能 |
2.异常抛出函数
为了使程序具有健壮性,MFC提供了多个异常抛出函数,如表1.7所示。
表1.7 异常抛出函数
函 数 名 称 | 描 述 |
AfxThrowArchiveException | 抛出一个档案异常 |
AfxThrowFileException | 抛出一个文件异常 |
续表
函 数 名 称 | 描 述 |
AfxThrowMemoryException | 抛出一个内存异常 |
AfxThrowNotSupportedException | 抛出一个不支持的异常 |
AfxThrowResourceException | 抛出一个Windows未找到资源的异常 |
AfxThrowUserException | 在用户初始化的程序动作中抛出一个异常 |
AfxThrowOleException | 抛出一个OLE异常 |
AfxThrowOleDispatchException | 在OLE自动化函数内抛出异常 |
AfxThrowDaoException | 从代码中抛出一个CDaoException异常 |
AfxThrowDBException | 从代码中抛出一个CDBException异常 |
3.字符串格式和消息框函数
MFC除了提供CString类操作字符串外,还提供了两个全局函数,如表1.8所示。
表1.8 字符串格式和消息框函数
函 数 名 称 | 描 述 |
AfxFormatString1 | 用一个字符串替换给定字符串中的格式字符“%1” |
AfxFormatString2 | 用两个字符串替换给定字符串中两个格式字符“%1”和“%2” |
AfxMessageBox | 显示一个消息框 |
4.应用程序信息和管理函数
MFC提供的与应用程序有关的全局函数如表1.9所示。其中,有许多函数在开发应用程序过程中经常使用。
表1.9 应用程序信息和管理函数
函 数 名 称 | 描 述 |
AfxFreeLibrary | 减少已调入内存的动态链接库模块的引用计数;当引用计数减到0时,该模块就会被释放 |
AfxGetApp | 返回应用程序对象CWinApp的一个指针 |
AfxGetAppName | 返回应用程序的名称 |
AfxGetInstanceHandle | 返回应用程序实例句柄 |
AfxGetMainWnd | 返回指向非OLE应用程序的当前主窗口指针,或者是服务器程序的线程框架窗口 |
AfxGetResourceHandle | 返回应用程序默认的资源 |
AfxInitRichEdit | 为应用程序初始化RichEdit控件 |
AfxLoadLibrary | 调入一个DLL模块,同时返回一个句柄,通过该句柄可以获得DLL中函数的地址 |
AfxRegisterWndClass | 注册一个Windows窗口类,用它来代替MFC自动注册的窗口类 |
AfxSocketInit | 在应用程序的InitInstance方法中调用,用于初始化套接字 |
AfxSetResourceHandle | 设置应用程序默认的资源句柄 |
AfxRegisterClass | 在使用MFC的DLL中注册窗口类 |
AfxBeginThread | 创建一个新的线程 |
AfxEndThread | 结束一个线程 |
AfxGetThread | 获取指向当前CWinThread对象的指针 |
AfxWinInit | 由MFC提供的WinMain函数直接调用,在GUI应用程序中,用于初始化MFC |
5.集合类帮助函数
集合类帮助函数多用于数组操作。表1.10列出了MFC提供的集合类帮助函数。
表1.10 集合类帮助函数
函 数 名 称 | 描 述 |
CompareElements | 比较元素是否相同 |
ConstructElements | 当生成一个元素时必须实现的动作 |
CopyElements | 将元素从一个数组中复制到另一个数组中 |
DestructElements | 当销毁一个数组时需要实现的动作 |
DumpElements | 提供了面向流的诊断输出 |
HashKey | 计算一个Hash键 |
SerializeElements | 将元素保存到文件中,或从文件中获得元素 |
6.记录字段交换函数
记录字段交换函数用于记录集数据与变量的交互。表1.11列出了与记录字段相关的函数。
表1.11 记录字段交换函数
函 数 名 称 | 描 述 |
RFX_Binary | 传送CByteArray类型的字节数 |
RFX_Bool | 传送布尔数据 |
RFX_Byte | 传送单个字节数据 |
RFX_Date | 传送CTime或TIMESTAMP_STRUCT类型的时间和日期数据 |
RFX_Double | 传送双精度浮点数据 |
RFX_Int | 传送整型数据 |
RFX_Long | 传送长整型数据 |
RFX_LongBinary | 通过CLongBinary类的对象传送二进制大对象 |
RFX_Single | 传送浮点数据 |
RFX_Text | 传送字符串数据 |
RFX_Binary_Bulk | 传送二进制数据的数组 |
RFX_Bool_Bulk | 传送布尔数据的数组 |
RFX_Date_Bulk | 传送TIMESTAMP_STRUCT数据的数组 |
RFX_Double_Bulk | 传送双精度浮点数据数组 |
RFX_Int_Bulk | 传送整型数据数组 |
RFX_Long_Bulk | 传送长整型数据数组 |
RFX_Single_Bulk | 传送浮点数据数组 |
RFX_Text_Bulk | 传送LPSTR数据数组 |
7.OLE相关函数
为了支持OLE技术,MFC对OLE进行了封装,同时提供了一些全局函数用于OLE操作,如表1.12所示。
表1.12 OLE相关函数
函 数 名 称 | 描 述 |
AfxOleInit | 初始化OLE库 |
AfxOleCanExitApp | 判断应用程序是否能够结束 |
AfxOleGetMessageFilter | 获取应用程序当前的消息过滤器 |
AfxOleGetUserCtrl | 获取当前的用户控制标记 |
续表
函 数 名 称 | 描 述 |
AfxOleSetUserCtrl | 设置或清除用户控制标记 |
AfxOleLockApp | 增加应用程序中活动对象的全局计数 |
AfxOleUnlockApp | 减少应用程序中活动对象的全局计数 |
AfxOleRegisterServerClass | 在OLE系统注册表中注册一个服务器 |
AfxOleSetEditMenu | 实现TypeName Object命令的用户接口 |
AfxOleRegisterControlClass | 在注册数据库中添加控件类 |
AfxOleRegisterPropertyPageClass | 在注册数据库中添加控件的属性页类 |
AfxOleRegisterTypeLib | 在注册数据库中添加控件的类型库 |
AfxOleUnregisterClass | 从注册数据库中删除控件类或属性页类 |
AfxOleUnregisterTypeLib | 从注册数据库中删除控件的类型库 |
8.Internet URL解析全局函数
为了获得URL字符串的相关信息,MFC提供了两个全局函数,如表1.13所示。
表1.13 Internet URL解析全局函数
函 数 名 称 | 描 述 |
AfxParseURL | 分析一个URL字符串,返回服务器的类型及内容 |
AfxParseURLEx | 分析一个URL字符串,返回服务器的类型及内容,同时返回系统用户的名字和密码 |
1.5.5 开发基于MFC的应用程序
在1.4.5节中介绍了利用Windows API开发Win32应用程序。本节将介绍如何开发基于MFC的应用程序。
实例位置:光盘/mingrisoft/sl/01/02/MFCApp.dsw
具体步骤如下。
(1)单击“File/New”菜单项,打开“New”对话框,选择“Projects”选项卡,如图1.11所示。
图1.11 MFC应用程序
(2)在列表中选择“MFC AppWizard(exe)”选项,在“Project name”编辑框中输入工程名称,此时“OK”按钮变为可用,单击该按钮进入“MFC AppWizard-Step1”对话框,如图1.12所示。
图1.12 “MFC AppWizard-Step1”对话框
(3)在“MFC AppWizard-Step1”对话框中选择应用程序的类型。这里共有3种类型可供选择,分别为“Single document(单文档应用程序)”、“Multiple documents(多文档应用程序)”和“Dialog based(对话框应用程序)”。这里选择“Dialog based”,即创建基于对话框的应用程序。
(4)单击“Next”按钮,进入“MFC AppWizard-Step 2 of 4”对话框,如图1.13所示。
图1.13 “MFC AppWizard-Step 2 of 4”对话框
(5)保留默认的设置,单击“Next”按钮,进入“MFC AppWizard-Step 3 of 4”对话框,如图1.14所示。
(6)单击“Next”按钮,进入“MFC AppWizard-Step 4 of 4”对话框,在该对话框中列出了MFC向导创建的类,如图1.15所示。
图1.14 “MFC AppWizard-Step 3 of 4”对话框
图1.15 “MFC AppWizard-Step 4 of 4”对话框
(7)单击“Finish”按钮,完成工程的创建。运行程序,效果如图1.16所示。
图1.16 MFC应用程序
使用MFC应用程序向导,没有编写一句代码,就创建出了一个对话框。与1.4.5节中的程序开发过程相比,简单了许多,这就是MFC的优势。
但是,读者似乎会产生疑问,在上面的工程中并没有发现程序的入点函数WinMain。下面将剖析程序的执行过程,读者在其中会找到答案。
在编译应用程序的时候,首先会构造工程中的全局对象,即先创建全局对象。在上面创建的工程中,读者可以找到MFC应用程序向导创建的全局对象theApp。因此会执行theApp类的构造函数,接着链接器加载代码,执行_tWinMain函数。用户可以按<F11>键进入到_tWinMain函数,如图1.17所示。
图1.17 _tWinMain函数
_tWinMain函数与WinMain函数有什么关系呢?将光标定位在“_tWinMain”处,按<F12>键查看其定义,发现_tWinMain其实是一个宏,等价于WinMain函数,如图1.18所示。
图1.18 _tWinMain宏定义
在_tWinMain函数中,调用了AfxWinMain函数,而AfxWinMain函数又调用了AfxWinInit函数,然后又调用了应用程序的InitApplication方法、InitInstance方法,如图1.19所示。
图1.19 AfxWinMain函数
在图1.19中注意“pThread->InitInstance()”语句,其中pThread是一个CWinThread对象指针,通过调用AfxGetThread函数获得。上文中笔者说在AfxWinMain函数中调用了应用程序的InitInstance方法,应用程序与pThread有何关系呢?其实应用程序CWinApp的父类就是CWinThread。在CWinThread类中定义了虚拟方法InitInstance,依据动态绑定的原则,“pThread->InitInstance()”语句实际上调用的是应用程序的InitInstance方法。本例中InitInstance方法代码是由MFC应用程序向导自动生成的,代码如下:
BOOL CMFCAppApp::InitInstance()
{
AfxEnableControlContainer();
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
CMFCAppDlg dlg; //定义一个对话框对象
m_pMainWnd = &dlg; //将对话框设置为应用程序的主窗口
int nResponse = dlg.DoModal(); //创建一个模态对话框
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
在应用程序的InitInstance方法中,对话框dlg调用了DoModal方法,该方法用于创建一个模态对话框。DoModal方法在内部调用一系列的函数,实现窗口类的注册、创建窗口、进入消息循环等功能。有关模态对话框的相关知识在后面章节会详细介绍。