MFC 类别主要可分为下列数大群组:
■ General Purpose classes - 提供字符串类别、数据处理类别(如数组与串行),异
常情况处理类别、文件类别...等等。
■ Windows API classes - 用来封包Windows API,例如窗口类别、对话框类别、
DC 类别...等等。
■ Application framework classes - 组成应用程序骨干者, 即此组类别, 包括
Document/View、消息邦浦、消息映射、消息绕行、动态生成、文件读写等等。
■ high level abstractions - 包括工具栏、状态列、分裂窗口、卷动窗口等等。
■ operation system extensions - 包括OLE、ODBC、DAO、MAPI、WinSock、ISAPI
等等
CObject
绝大部份类别库,往往以一个或两个类别,做为其它绝大部份类别的基础。MFC 亦复如
此。CObject 是万类之首,凡类别衍生自CObject 者,得以继承数个对象导向重要性质,
包括RTTI(执行时期型别鉴识)、Persistence(对象保存)、Dynamic Creation(动态生
成)、Diagnostic(错误诊断)。本书第3章对于这些技术已有了一份DOS 环境下的模
拟,第8章另有MFC 相关源代码的探讨。其中,「对象保存」又牵扯到CArchive,「诊
断」又牵扯到CDumpContext,「执行时期型别鉴识」以及「动态生成」又牵扯到
CRuntimeClass。
数据处理类别(collection classes)
所谓collection,意指用来管理一「群」对象或标准类型的资料。这些类别像是Array 或List
或Map 等等,都内含针对元素的「加入」或「删除」或「巡访」等成员函数。Array
(数组)和List(串行)是数据结构这门课程的重头戏,大家比较熟知,Map(可视之
为表格)则是由成双成对的两两对象所构成,使你很容易由某一对象得知成对的另一物
件;换句话说一个对象是另一个对象的键值(key)。例如,你可以使用String-to-String
Map,管理一个「电话-人名」数据库;或者使用Word-to-Ptr Map,以16 位数值做为
一个指针的键值。
最令人侧目的是,由于这些类别都支持Serialization,一整个数组或串行或表格可以单一一进程序代码就写到文件中(或从文件读出)。第8章的Scribble Step1 范例程序中你就会
看到它的便利。
MFC 支持的collection classes 有:
一进程序代码就写到文件中(或从文件读出)。第8章的Scribble Step1 范例程序中你就会
看到它的便利。
CRect - 封装Windows 的RECT 结构。这个类别在Windows 环境中特别有用,
因为CRect 常常被用作MFC 类别成员函数的参数。
■ CSize - 封装Windows 的SIZE 结构。
■ CPoint - 封装Windows 的POINT 结构。这个类别在Windows 环境中特别有用
因为CPoint 常常被用作MFC 类别成员函数的参数。
■ CTime - 表现绝对时间, 提供许多成员函数, 包括取得目前时间( static
GetCurrentTime)、将时间资料格式化、抽取特定字段(时、分、秒)等等。它
对于+、-、+=、-+ 等运算子都做了多载动作。
■ CTimeSpan - 以秒数表现时间,通常用于计时码表。提供许多成员函数,包括把
秒数转换为日、时、分、秒等等。
<!--[if !supportLists]-->■ <!--[endif]-->CString - 用来处理字符串。支持标准的运算子如=、+=、< 和>。
异常处理类别(exception handling classes)
所谓异常情况(exception),是发生在你的程序执行时期的不正常情况,像是文件打不
开、内存不足、写入失败等等等。我曾经在第2章最后面介绍过异常处理的观念及相
关的MFC 类别,并在第4章「Exception Handling」一节介绍过一个简单的例子。与「异常处理」
Windows API classes
这是MFC 声名最著的一群类别。如果你去看看源代码,就会看到这些类别的成员函数
所对应的各个Windows API 函数。
■ CWinThread - 代表MFC 程序中的一个执行线程。自从3.0 版之后,所有的MFC
类别就都已经是thread-safe 了。SDK 程序中标准的消息循环已经被封装在此
一类别之中(你会在第6章看到我如何把这一部份开膛剖肚)。
■ CWinApp - 代表你的整个MFC 应用程序。此类别衍生自CWinThread;要知
道,任何32 位Windows 程序至少由一个执行线程构成。CWinApp 内含有用
的成员变量如m_szExeName, 放置执行档档名, 以及有用的成员函数如
ProcessShellCommand,处理命令列选项。
■ CWnd - 所有窗口,不论是主框窗口、子框窗口、对话框、控制组件、view 视
窗,都有一个对应的C++ 类别,你可以想象「窗口handle」和「C++ 对象」
结盟。这些C++ 类别统统衍生自CWnd,也就是说,凡衍生自CWnd 之类别才
能收到WM_ 窗口消息(WM_COMMAND 除外所谓「窗口handle」和「C++ 对象」结盟,实际上是CWnd 对象有一个成员变
数m_hWnd,就放着对应的窗口handle。所以,只要你手上有一个CWnd 对象
或CWnd 对象指针,就可以轻易获得其窗口handle:
HWND hWnd = pWnd->m_hWnd;
■ CCmdTarget - CWnd 的父类别。衍生自它, 类别才能够处理命令消息
WM_COMMAND。这个类别是消息映射以及命令消息绕行的大部份关键,我将
在第9章推敲这两大神秘技术。
<!--[if !supportLists]-->■ <!--[endif]-->GDI 类别、DC 类别、Menu 类别。
Application framework classes
这一部份最为人认知的便是Document/View,这也是使MFC 跻身application framework
的关键。Document/View 的观念是希望把资料的本体,和资料的显像分开处理。由于文
件产生之际,必须动态生成Document/View/Frame 三种对象,所以又必须有所谓的
Document Template 管理之。
■ CDocTemplate、CSingleDocTemplate、CMultiDocTemplate - Document Template 扮演
黏胶的角色,把Document 和View 和其Frame(外框窗口)胶黏在一块儿。
■ CSingleDocTemplate 一次只支持一种文件类型,CMultiDocTemplate 可同时支持多
种文件类型。注意,这和MDI 程序或SDI 程序无关,换句话说,MDI 程序
也可以使用CSingleDocTemplate,SDI 程序也可以使用CMultiDocTemplate。
但是,逐渐地,MDI 这个字眼与它原来的意义有了一些出入(要知道,这个字眼早
在SDK 时代即有了)。因此,你可能会看到有些书籍这么说:MDI 程序使用
CMultiDocTemplate,SDI 程序使用CSingleDocTemplate。
■ CDocument - 当你为自己的程序由CDocument 衍生出一个子类别后,应该在其
中加上成员变量,以容纳文件资料;并加上成员函数,负责修改文件内容以及
读写档。读写文件由虚拟函数Serialize 负责。第8章的Scribble Step1 范例程序
有极佳的示范。
■ CView - 此类别负责将文件内容呈现到显示装置上:也许是屏幕,也许是打印
机。文件内容的呈现由虚拟函数OnDraw 负责。由于这个类别实际上就是你在
屏幕上所看到的窗口(外再罩一个外框窗口),所以它也负责使用者输入的第
一线服务。例如第8章的Scribble Step1 范例,其View 类别便处理了鼠标的
按键动作
High level abstractions
视觉性UI 对象属于此类,例如工具栏CToolBar、状态列CStatusBar、对话框列
CDialogBar。加强型的View 也属此类,如可卷动的ScrollView、以对话框为基础的
CFormView、小型文字编辑器CEditView、树状结构的CTreeView,支持RTF 文件格式
的CRichEditView 等等。
Afx 全域函数
还记得吧,C++ 并不是纯种的对象导向语言(SmallTalk 和Java 才是)。所以,MFC
之中得以存在有不属于任何类别的全域函数,它们统统在函数名称开头冠以Afx。
下面是几个常见的Afx 全域函数:
函数名称 说明
AfxWinInit 被WinMain(由MFC 提供)调用的一个函数,用做MFC GUI
程序初始化的一部份,请看第6章的「AfxWinInit - AFX 内部
初始化动作」一节。如果你写一个MFC console 程序,就得
自行调用此函数(请参考Visual C++ 所附之Tear 范例程序)。
AfxBeginThread 开始一个新的执行线程(请看第14 章,# 756 页)。
AfxEndThread 结束一个旧的执行线程(请看第14 章,# 756 页)。
AfxFormatString1 类似printf 一般地将字符串格式化。
AfxFormatString2 类似printf 一般地将字符串格式化。
AfxMessageBox 类似Windows API 函数MessageBox。
AfxOutputDebugString 将字符串输往除错装置(请参考附录D,# 924 页)。
AfxGetApp 取得application object(CWinApp 衍生对象)的指针。
AfxGetMainWnd 取得程序主窗口的指针。
AfxGetInstance 取得程序的instance handle。
AfxRegisterClass 以自定的WNDCLASS 注册窗口类别(如果MFC 提供的数个
窗口类别不能满足你的话)。
MFC 宏(macros)
CObject 和CRuntimeClass 之中封装了数个所谓的object services,包括「取得执行时期
的类别信息」(RTTI)、Serialization(文件读写)、动态产生对象...等等。所有衍生自CObject的类别,都继承这些机能。我想你对这些名词及其代表的意义已经不再陌生-- 如果你没
有错过第3章的「MFC 六大技术仿真」的话。
■ 取得执行时期的类别信息(RTTI),使你能够决定一个执行时期的对象的类别
信息,这样的能力在你需要对函数参数做一些额外的类型检验,或是当你要针
对对象属于某种类别而做特别的动作时,份外有用。
■ Serialization 是指将对象内容写到文件中,或从文件中读出。如此一来对象的
生命就可以在程序结束之后还延续下去,而在程序重新激活之后,再被读入。
这样的对象可说是"persistent"(永续存在)。
■ 所谓动态的对象生成(Dynamic object creation),使你得以在执行时期产生一
个特定的对象。例如document、view、和frame 对象就都必须支持动态对象
生成,因为framework 需要在执行时期产生它们(第8章有更详细的说明)。
此外,OLE 常常需要在执行时期做对象的动态生成动作。例如一个OLE server 程序必
须能够动态产生OLE items,用以反应OLE client 的需求。
MFC 针对上述这些机能,准备了一些宏,让程序能够很方便地继承并实作出上述四大
机能。这些宏包括:
宏名称 提供机能 出现章节
DECLARE_DYNAMIC 执行时期类别信息 第3章、第8章
IMPLEMENT_DYNAMIC 执行时期类别信息 第3章、第8章
DECLARE_DYNCREATE 动态生成 第3章、第8章
IMPLEMENT_DYNCREATE 动态生成 第3章、第8章
DECLARE_SERIAL 对象内容的文件读写 第3章、第8章
IMPLEMENT_SERIAL 对象内容的文件读写 第3章、第8章
DECLARE_OLECREATE OLE 对象的动态生成 不在本书范围之内
IMPLEMENT_OLECREATE OLE 对象的动态生成 不在本书范围之内
我也已经在第3章提过MFC 的消息映射(Message Mapping)与命令绕行(Command
Routing)两个特性。这两个性质系由以下这些MFC 宏完成:
宏名称 提供机能 出现章节
DECLARE_MESSAGE_M A P 声明消息映射表数据结构 第3章、第9章
BEGIN_MESSAGE_MAP 开始消息映射表的建置 第3章、第9章
ON_COMMAND 增加消息映射表中的项目 第3章、第9章
ON_CONTROL 增加消息映射表中的项目 本书未举例
ON_MESSAGE 增加消息映射表中的项目 ???
ON_OLECMD 增加消息映射表中的项目 本书未举例
ON_REGISTERED_MESSAGE 增加消息映射表中的项目 本书未举例
ON_REGISTERED_THREAD_
MESSAGE
增加消息映射表中的项目 本书未举例
ON_THREAD_MESSAGE 增加消息映射表中的项目 本书未举例
ON_UPDATE_COMMAND_UI 增加消息映射表中的项目 第3章、第9章
END_MESSAGE_MAP 结束消息映射表的建置 第3章、第9章
事实上,与其它MFC Programming 书籍相比较,本书最大的一个特色就是,要把上述
这些MFC 宏的来龙去脉交待得非常清楚。我认为这对于撰写MFC 程序是非常重要
的一件事。
MFC 数据类型(data types)
下面所列的这些数据类型,常常出现在MFC 之中。其中的绝大部份都和一般的Win32
程序(SDK 程序)所用的相同。
下面这些是和Win32 程序(SDK 程序)共同使用的数据类型:
数据类型意义
BOOL Boolean 值(布尔值,不是TRUE 就是FALSE)
BSTR 32-bit 字符指针
BYTE 8-bit 整数,未带正负号
COLORREF 32-bit 数值,代表一个颜色值
DWORD 32-bit 整数,未带正负号
LONG 32-bit 整数,带正负号
LPARAM 32-bit 数值,做为窗口函数或callback 函数的一个参数
LPCSTR 32-bit 指针,指向一个常数字符串
LPSTR 32-bit 指针,指向一个字符串
LPCTSTR 32-bit 指针,指向一个常数字符串。此字符串可移植到Unicode 和DBCS(双
字节字集)
LPTSTR 32-bit 指针,指向一个字符串。此字符串可移植到Unicode 和DBCS(双位
组字集)
LPVOID 32-bit 指针,指向一个未指定类型的资料
LPRESULT 32-bit 数值,做为窗口函数或callback 函数的回返值
UINT 在Win16 中是一个16-bit 未带正负号整数,在Win32 中是一个32-bit
未带正负号整数。
WNDPROC 32-bit 指针,指向一个窗口函数
WORD 16-bit 整数,未带正负号
WPARAM 窗口函数的callback 函数的一个参数。在Win16 中是16 bits,在Win32
中是32 bits。
下面这些是MFC 独特的数据类型:
数据类型 意义
POSITION 一个数值,代表collection 对象(例如数组或串行)中的元素位置。常使用
于MFC collection classes。
LPCRECT 32-bit 指针,指向一个不变的RECT 结构。
前面所说那些MFC 数据类型与C++ 语言数据类型之间的对应,定义于WINDEF.H
中。我列出其中一部份,并且将不符合(_MSC_VER >= 800) 条件式的部份略去。