《MFC(C教程PPT课件)》由会员分享,可在线阅读,更多相关《MFC(C教程PPT课件)(69页珍藏版)》请在金锄头文库上搜索。
1、1 Windows编程机制(编程机制(API) Windows是一个多进程的图形窗口操作系统,Windows应用程序与DOS应用程序有很大的区别。DOS应用程序采用顺序执行过程,而Windows是一个基于事件的消息(Message)驱动系统。Windows应用程序是按照“事事件件消消息息处处理理”非顺序的机制运行。当有某个事件(如单击鼠标、键盘输入和执行菜单命令等)发生时,Windows会根据具体的事件产生对应的消息,并发送到指定应用程序的消息队列;应用程序从消息队列中取出消息,并根据不同的消息进行不同的处理。WindowsAPI( ApplicationProgrammingInterfac
2、e)是Windows操作系统与应用程序之间的标准接口,它提供了上千个标准函数、宏和数据结构的定义。在使用VisualC+、VisualBasic和Delphi编程时都可以调用WindowsAPI函数,Windows应用程序可通过调用标准WindowsAPI函数使用系统提供的功能。WindowsAPI函数定义在一些DLL动态链接库中,其中最主要的DLL是User32.dll、Gdi32.dll和Kernel32.dll三个库文件。1.1 关于关于API和和SDK 传统SDK编程:程序员通过调用API函数,自己动手、按部就班地实现程序各部分的功能。SDK应用程序的结构比较清晰,但程序员必须编写所有
3、的功能代码。利用VisualC+编写一个类SDK应用程序:首先利用Win32Application向导建立一个Windows应用程序框架,然后根据需要可以向程序项目中添加一些头文件、实现源文件和资源文件,并编写具体的程序代码。例例编写一个名为Hello的类SDK应用程序,当单击鼠标时通过调用API函数,以弹出一个提示信息对话框。1执行“File|New”菜单命令,在New对话框的Project页面中选择Win32Application项目类型,输入程序名Hello。在向导第1步选择“Atypital“Helloworld!”application”项,单击Finish按钮。2在窗口函数WndP
4、roc()的消息处理分支switch-case结构中添加WM_LBUTTONDOWN鼠标单击消息及其处理代码:caseWM_LBUTTONDOWN:MessageBox(NULL,Youpressedtheleft button of mouse!,Message,NULL);break; SDK应用程序结构:l一个由API函数构造的Windows程序的功能由三个部分组成:入口函数、窗口函数和Windows系统。l每一个程序都有一个主函数,WinMain()函数就是Windows程序的入口主函数。该函数的主要任务是完成一些初始化工作并维护一个消息循环。当消息循环结束后,就退出了WinMain(
5、)函数,也就退出了应用程序。此外,WinMain()函数还负责完成窗口的注册、创建和显示。lWindows程序以窗口的形式存在,在不同窗口之间传递消息是Windows和应用程序进行交流的主要形式。程序具体功能由不同的窗口函数实现。1.2 句柄句柄 在Windows中,用句柄(句柄(Handle)标识应用程序中不同的对象和同类对象中不同的实例,如一个具体的窗口、按钮、输出设备、画笔和文件等。通过句柄可以获得相应的对象信息。常用的句柄类型有:HWND、HINSTANCE、HDC、HCURSOR、HICON、HMENU等。句柄常作为Windows消息和API函数的参数,在采用API方法编写Windo
6、ws应用程序时要经常使用句柄。而采用MFC方法编写Windows应用程序时,由于对应的MFC类已对句柄进行了封装,大多数情况下不再需要访问句柄。获取获取MFC类对象的句柄的两种方法:类对象的句柄的两种方法: l通过访问类的public属性成员变量,如类CWnd的成员变量m_hWnd就是一个窗口对象的句柄。l先定义一个句柄,然后调用MFC类的成员函数Attach()将句柄与一个MFC类对象联系在一起,此时的句柄就成为该MFC类对象的句柄。在退出对象作用域之前,调用成员函数Detach()将句柄和对象进行分离。如下所示:CWndmyWnd;HWNDhWnd;myWnd.Attach(hWnd);.
7、myWnd.Detach();l所谓消消息息就是用于描述某个事件发生的信息,而事事件件是对于Windows的某种操作。l事件和消息密切相关,事件是因,消息是果,事件产生消息,消息对应事件。所谓消息的响应,其实质就是事件的响应。l消消息息驱驱动动是Windows应用程序的核心,所有的外部响应(如键盘、鼠标和计时器等)都被Windows先拦截,转换成消息后再发送到应用程序中的目标对象,应用程序根据消息的具体内容进行处理。l消息不仅可由Windows发出,它也可由应用程序本身或其它程序产生。Windows为每一个应用程序都维护一个或多个消消息息队队列列,发送到每个程序窗口的消息都排成一个队列。1.3
8、 事件和事件和消息消息 Windows消息消息分为三种类型:分为三种类型:l标标 准准 Windows消消 息息 : 以 WM_前 缀 ( 但 不 包 括WM_COMMAND)开始的消息,包括鼠标消息、键盘消息和窗口消息,如WM_MOVE、WM_PAINT等。l控控件件通通知知(Control Notification)消消息息:对控件操作引起的 消 息 , 是 控 件 和 子 窗 口 向 其 父 窗 口 发 出 的WM_COMMAND通知消息。例如,当用户修改了编辑控件 中 的 文 本 后 , 编 辑 控 件 向 其 父 窗 口 发 送WM_COMMAND通知消息。l命命令令(Command
9、)消消息息:由菜单项、工具栏按钮、快捷键等用户界面对象发出的WM_COMMAND消息。命令消息与其它消息不同,它可被更广泛的的对象如文档、文档模板、应用程序对象、窗口和视图等处理。typedefstructtagMSGHWNDhWnd;/目标窗口句柄UINTmessage;/消息标识WPARAMwParam;/消息参数1(附加信息,16位)LPARAMlParam;/消息参数2(附加信息,32位)DWORDtime;/消息发送时间POINTpt;/消息发送时鼠标的屏幕坐标MSG;消息用消息用MSG结构表示:结构表示: 例例 为程序Hello添加键盘消息处理功能,判断当前按下的键是不是A或a键,
10、并给出相应的提示。打开程序项目Hello,在文件Hello.cpp的窗口函数WndProc()的switch消息处理分支中添加键盘消息的处理代码:caseWM_KEYDOWN:/处理键盘消息if(wParam=0x41)/A或a键的虚键码为0x41HMessageBox(NULL,ThekeyyoupressedisAora!,KEYDOWN,NULL);elseMessageBox(NULL,ThekeyyoupressedisnotAora!,KEYDOWN,NULL);break; 利用Windows API开发程序的用户有这样的体会,即使开发一个简单的Windows应用程序也需要对Wi
11、ndows的编程原理有很深刻的认识,需要手工编写冗长的代码。由于程序的出错率是随着代码长度的增加呈几何级数增长的,而且当程序长度逐渐膨胀时,调试程序会变得越来越困难。因此,传统的Windows应用程序设计需要程序员有极大的耐心和丰富的编程经验。 VisualC+捆绑了微软的基础类MFC,编程时我们就可以利用类的可重用性和可扩充性,大大降低Windows应用程序设计的难度和工作量。2MFC的基本原理和使用方法的基本原理和使用方法2节要点节要点2.1MFC类(对应教材的第五章全部内容)类(对应教材的第五章全部内容)2.2MFC应用程序框架应用程序框架2.3MFC消息管理消息管理2.4MFC宏宏一、
12、一、 MFC概述(教材概述(教材1 1节)节)类库是一个可以在应用程序中使用的相互关联的C+类的集合。MFC作为一个Windows编程类库,它包含了200多个类,封装了Windows的大部分编程对象以及与它们有关的操作。虽然程序在功能上千差万别,但从本质上看,都可以分为用户界面设计、文件操作、数据库访问及多媒体使用等几部分,这些都可以通过一些类来实现。MFC提供了一个标准化的程序结构,使开发人员不必从头设计一个Windows应用程序。MFC实际上是一个庞大的文件库,它由几百个执行文件和源代码文件(如H文件)组成。2.1MFC类(对应教材的第五章全部内容)类(对应教材的第五章全部内容) 在Mic
13、rosoft推出Windows3.0之后,Windows操作系统受到越来越多的人的青睐。因此当时学习Windows程序设计便成为程序员的显学。但是不久,开发人员发现撰写Windows应用程序是重复、厌烦的过程,而且效率极低。因为每一个窗口都需要在窗口函数中处理大量的消息,导致出现大量的重复代码。随着C+语言的盛行,开发人员发现利用C+语言的特性来封装WindowsAPI和Windows应用程序的开发可以明显地提高程序的开发效率。于是,人们对这种新的开发模式有了强烈的需求。在这种情况下,Microsoft和其他一些大的厂商开始从事Windows框架的设计。于是,MFC就是在这种背景下诞生了。MF
14、C英文全称是MicrosoftFundationClassLibrary,即微软基础类库。从其产生至今已经发展了十几个版本,表5-1显示的MFC的发展过程。MFCVisual C+MFC1.0版本MicrosoftC/C+7.0版本MFC2.0版本VisualC+1.0版本MFC2.5版本VisualC+1.5版本MFC3.0版本VisualC+2.0版本MFC3.1版本VisualC+2.1版本MFC3.2版本VisualC+2.2版本MFC4.0版本VisualC+4.0版本MFC4.1版本VisualC+4.1版本MFC4.2版本VisualC+4.2版本MFC4.21版本VisualC
15、+0版本MFC6.0版本VisualC+6.0版本MFC7.0版本VisualC+.NET2002版本MFC7.1版本VisualC+.NET2003版本MFC8.0版本VisualC+.NET2005版本 MFC中的类按照功能的不同,可以分为不同的层次。图5-1显示了MFC中的所有类及类层次。二、二、 MFC类库层次(教材类库层次(教材2 2节)节) MFC中的数据类型与WindowsSDK开发包中的数据类型多数是一致的。但也有一些数据类型是MFC独有的。表5-2列出了MFC中使用的数据类型。 数据类型数据类型描述描述BOOL布尔值,取值范围TRUE和FALSEBSTR32位字符指针BYTE
16、8位无符号整数COLORREF用作颜色值的32位数值DWORD32位无符号整数,或者段的地址和与之相关的偏移量LONG32位有符号整数LPARAM32位值,作为窗口函数或回调函数的参数LPCSTR指向字符串常量的32位指针LPSTR32位字符串指针LPCTSTR指向兼容Unicode和DBCS字符集的字符串常量32位指针LPTSTR指向兼容Unicode和DBCS字符集的字符串32位指针LPVOID指向一个未定义类型的32位指针LRESULT窗口函数或回调函数返回的32位值UINT32位无符号整数WNDPROC指向一个窗口函数的32位指针WORD16位无符号整数WPARAM作为参数传递给窗口函
17、数或回调函数的值POSITION用于标记集合中一个元素的位置LPCRECT指向一个RECT结构体常量的32位指针 其中,表格中列出的最后两个数据类型是MFC所特有的。三、三、 MFC常用数据类型(教材常用数据类型(教材3 3节)节) 在MFC类库中,除了提供实现各种功能的类外,还提供了许多全局函数。如下所示:1MFC诊断函数诊断函数为了调试的方便,MFC提供了多个诊断函数,如表5-3所示。函数名称函数名称描述描述AfxCheckMemory检查当前分配的所有内存的完整性AfxDump如果在调试器内调用,则转存对象的状态AfxDumpStack生成当前栈的一个映像,该函数通常被静态链接AfxEn
18、ableMemoryTracking打开或关闭内存跟踪AfxIsMemoryBlock检验一个内存块是否被正确的分配AfxIsValidString检验一个字符串指针是否有效AfxSetAllocHook允许在每次进行内存分配时调用一个函数AfxDoForAllClasses对所有从CObject继承的支持运行时检查的类执行一个特定的功能AfxDoForAllObjects对所有从CObject继承的用new分配内存对象执行一个指定的功能四、四、 MFC全局函数(教材全局函数(教材4 4节)节)MFC全局函数 2异常抛出函数异常抛出函数为了使程序更具有健壮性,MFC提供了多个异常抛出函数,如表
19、5-4所示。函数名称函数名称描述描述AfxThrowArchiveException抛出一个档案异常AfxThrowFileException抛出一个文件异常AfxThrowMemoryException抛出一个内存异常AfxThrowNotSupportedException抛出一个不支持的异常AfxThrowResourceException抛出一个Windows未找到资源异常AfxThrowUserException在用户初始化的程序动作中抛出一个异常AfxThrowOleException抛出一个OLE异常AfxThrowOleDispatchException在OLE自动化函数内抛出
20、异常AfxThrowDaoException从代码中抛出一个CDaoException异常AfxThrowDBException从代码中抛出一个CDBException异常MFC全局函数3字符串格式和消息框函数字符串格式和消息框函数MFC除了提供CString类操作字符串外,还提供了两个全局函数,如表5-5所示。函数名称函数名称描述描述AfxFormatString1用一个字符串替换给定字符串中的格式字符“%1”AfxFormatString2用两个字符串替换给定字符串中两个格式字符“%1”和“%2”AfxMessageBox显示一个消息框4应用程序信息和管理函数应用程序信息和管理函数MFC提
21、供与应用程序有关的全局函数如表5-6所示。其中,有许多函数在开发应用程序过程中经常使用。MFC全局函数函数名称函数名称描述描述AfxFreeLibrary减少已调入内存的动态链接库模块的引用计数,当引用计数减到0时,该模块就会被释放AfxGetApp返回应用程序对象CWinApp的一个指针AfxGetAppName返回应用程序的名称AfxGetInstanceHandle返回应用程序实例句柄AfxGetMainWnd返回指向非OLE应用程序的当前主窗口指针,或者是服务器程序的线厂框架窗口AfxGetResourceHandle返回应用程序默认的资源AfxInitRichEdit为应用程序初始化
22、RichEdit控件AfxLoadLibrary调入一个DLL模块,同时返回一个句柄,通过该句柄可以获得DLL中函数的地址AfxRegisterWndClass注册一个Windows窗口类,用它来代替MFC自动注册的窗口类AfxSocketInit在应用程序的InitInstance方法中调用,用于初始化套接字AfxSetResourceHandle设置应用程序默认的资源句柄AfxRegisterClass在使用MFC的DLL中注册窗口类AfxBeginThread创建一个新的线程AfxEndThread结束一个线程AfxGetThread获取指向当前CWinThread对象的指针AfxWin
23、Init由MFC提供的WinMain函数直接调用,在GUI应用程序中,用于初始化MFCMFC全局函数集合类帮助函数集合类帮助函数集合类帮助函数多用于数组操作,表5-7列出了MFC提供的集合类帮助函数。函数名称函数名称描述描述CompareElements比较元素是否相同ConstructElements当生成一个元素时必须实现的动作CopyElements将元素从一个数组中拷贝到另一个数组中DestructElements当销毁一个数组时需要实现的动作DumpElements提供了面向流的诊断输出HashKey计算一个Hash键SerializeElements将元素保存到文件中,或从文件中获
24、得元素6记录字段交换函数记录字段交换函数记录字段交换函数用于记录集数据与变量的交互。表5-8列出了与记录字段相关的函数。MFC全局函数函数名称函数名称描述描述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
25、_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数据数组MFC全局函数7OLE相关函数相关函数为了支持OLE技术,MFC对OLE进行了封装,同时提供了一些全局函数用于OLE操作,如表5-9所示。函数名称函数名称描述描述AfxOleInit初始化OLE库AfxOleC
26、anExitApp判断应用程序是否能够结束AfxOleGetMessageFilter获取应用程序当前的消息过滤器AfxOleGetUserCtrl获取当前的用户控制标记AfxOleSetUserCtrl设置或清除用户控制标记AfxOleLockApp增加应用程序中活动对象的全局计数AfxOleUnlockApp减少应用程序中活动对象的全局计数AfxOleRegisterServerClass在OLE系统注册表中注册一个服务器AfxOleSetEditMenu实现TypeNameObject命令的用户接口AfxOleRegisterControlClass在注册数据库中添加控件类AfxOleR
27、egisterPropertyPageClass在注册数据库中添加控件的属性页AfxOleRegisterTypeLib在注册数据库中添加控件的类型库AfxOleUnregisterClass从注册数据库中删除控件类或属性页类AfxOleUnregisterTypeLib从注册数据库中删除控件的类型库MFC全局函数8Internet URL解析全局函数解析全局函数为了获得URL字符串相关信息,MFC提供了两个全局函数,如表5-10所示。函数名称函数名称描述描述AfxParseURL分析一个URL字符串,返回服务器的类型及内容AfxParseURLEx分析一个URL字符串,返回服务器的类型及内容
28、,同时返回系统用户的名字和密码特点:1)全局函数2)都是以名为Afx开头的函数那是历史遗留下的原因:那是历史遗留下的原因:long,longago,MFC不叫MFC(在MFC1.0以前),MS开发过一个失败的产品Afx,据说是ApplicationFramework的意思,(x是为了读起来响亮一点(微软就喜欢这样),例如ActiveX)是从上向下设计的(针对WINAPI).但没有成功.所以,有了MFC(从下向上封装api).就是说mfc的前身是afx.但很多mfc函数保留了以前的命名方式.二、二、 MFC体系结构体系结构MFC主要组成部分:类、宏和全局函数。类是MFC中最主要的内容。MFC类是
29、以层次结构方式组织起来的。MFC中的类分成两部分,除了一些辅助类,大多数的MFC类是直接或间接从根类CObject派生而来。几乎每一个派生层次都与一具体的Windows实例相对应,如文档类、窗口类和视图类等。MFC宏主要功能:消息映射、运行时对象类型服务、诊断服务、异常处理。MFC约定:全局函数以“Afx”为前缀,全局变量以“afx”为前缀。首先要对Windows编程概念和API函数有一定的了解,如WindowsAPI有哪些功能和哪些常用的数据结构等。学会抽象地把握问题,不求甚解,不要一开始学习VisualC+就试图了解整个MFC类库。从理解和使用两个方面学习MFC,理解MFC应用程序的框架结
30、构。先大体上了解MFC的概念、组成和基本约定,从简单的类入手,结合程序设计,由浅入深,循序渐进、日积月累。编程时如果MFC某个类能完成所需要的功能,可以直接调用已有类的方法(成员函数)。否则,可以利用面向对象技术中的“继承”方法对MFC类的行为进行扩充和修改,从MFC中已有的类派生出自己需要的类。学习MFC,另一点就是不要过分依赖于向导(Wizard)工具。向导能做许多工作,但同时掩饰了太多的细节。三、三、 学习学习MFC的方法的方法本节介绍基于MFC的应用程序开发。步骤如下:(1)单击“File/New”菜单项,打开“New”窗口,选择“Projects”选项卡,如图5-2所示。(2)在列表
31、中选择“MFCAppWizard(exe)”选项,在“Projectname”编辑框中输入工程名称,此时“OK”按钮变为可用,单击该按钮进入“MFC应用程序向导步骤1”窗口,如图5-3所示。五、五、 开发开发MFC应用程序(教材应用程序(教材5 5节)节)开发MFC应用程序(3)在“MFC应用程序向导步骤1”窗口中选择应用程序的类型。这里共有3种类型可供选择,依此为“单文档应用程序”、“多文档应用程序”和“对话框应用程序”,这里选择“Dialogbased”,即创建“基于对话框的应用程序”。(4)单击“Next”按钮进入“MFC应用程序向导步骤2”窗口,如图5-4所示。开发MFC应用程序(5)
32、保留默认的设置,单击“Next”按钮进入“MFC应用程序向导步骤3”窗口,如图所示。开发MFC应用程序(6)单击“Next”按钮进入“MFC应用程序向导步骤4”窗口,在该窗口中列出了MFC向导创建的类,如图5-6所示。开发MFC应用程序(7)单击“Finish”按钮完成工程的创建。运行程序,效果如图5-7所示。开发MFC应用程序使用MFC应用程序向导,没有编写一句代码,就创建出了一个对话框,这就是MFC给开发人员带来的便利。尽管每个应用程序具体实现的功能不同,但同一类程序的基本结构是相同的。因此,通常采用MFC AppWizard创建一个MFC应用程序框架应用程序框架。MFC不仅仅是一个类库,
33、它还提供了一层建立在MFC类对象封装上的附加应用程序框架。应用程序框架是为了生成一般的应用程序所必需的各种软组件的集成,是类库的一种超集。类库只是一种可以嵌入到任何程序中的、提供某些特定功能的类的集合。而应用程序框架却定制了应用程序的结构和源代码,其中的类对象既相互独立、又相互作用,形成一个统一的整体。2.2 MFC应用程序框架应用程序框架 MFC应用程序框架提供了构建应用程序所需要的类,在程序运行时能够生成运行时类的对象,如代表应用程序对象、文档对象、视图对象和框架窗口对象。应用程序对象theApp是一个唯一的全局变量,它的主要功能是通过调用WinMain()主函数启动程序的运行。MFC应
34、用 程 序 框 架 也 有 一 个 作 为 程 序 入 口 点 的 WinMain()主函数,但在源程序中看不见该函数,它在MFC中已定义好并同应用程序相链接。1)应用程序框架中的对象应用程序框架中的对象MFC应用程序对象之间的关系应用程序对象之间的关系 在MFC应用程序的CWinApp派生类对象theApp是一个全局变量,代表了应用程序运行的主线程。它在程序整个运行期间都存在,它的销毁意味着运行程序的消亡。MFC应用程序启动时,首先创建应用程序对象theApp,这时将自动调用应用程序类的构造函数初始化对象theApp,然后由应用程序框架调用MFC提供的AfxWinMain()主函数。AfxW
35、inMain()主函数首先通过调用全局函数AfxGetApp()获取应用程序对象theApp的指针pApp,然后通过pApp调用应用程序对象的有关成员函数,完成程序的初始化和启动工作,最后调用成员函数Run(),进入消息循环。程序运行后将收到WM_PAINT消息,调用OnPaint()函数绘制客户区窗口。如果Run()收到WM_QUIT消息,则结束消息循环,然后调用函数ExitInstance(),结束程序运行。2) MFC应用程序的生与死应用程序的生与死 MFC应用程序运行后各函数的调用关系应用程序运行后各函数的调用关系 InitInstance()函数是派生类唯一需要重载的函数,它负责应用
36、程序的初始化,如初始化数据、创建文档模板、处理命令行以及显示应用程序主窗口。3) 常用的常用的MFC文件和库文件文件和库文件 文文 件件 名名 称称说说 明明afxwin.h声明MFC核心类afxext.hMFC扩展文件,声明工具栏、状态栏、拆分窗口等类afxdisp.h声明OLE类afxdtctl.h声明支持IE4公用控件的MFC类,如CImageList等afxcmn.h声明Windows公共控件类Mfc42.libMFCxx.DLL的导入函数库(Release版)Mfc42D.libMFCxx.DLL的导入函数库(Debug版)MfcS42.libMFCSxx.DLL的导入函数库(Sta
37、ticRelease版)MfcS42D.libMFCSxxD.DLL的导入函数库(StaticDebug版)Mfc42U.libMFCxxU.DLL的导入函数库(UnicodeRelease版)Mfc42UD.libMFCxxUD.DLL的导入函数库(UnicodeDebug版)MfcO42D.libMFCOxxD.DLL的导入函数库(OLEDebug版)MfcD42D.libMFCDxxD.DLL的导入函数库(DatabaseDebug版)Nafxcw.libMFC静态链接库(Release版)NafxcwD.libMFC静态链接库(Debug版)gdi32.libGDI32.DLL的导入函
38、数库user32.libUSER32.DLL的导入函数库kernel32.libKERNEL32.DLL的导入函数库msvcrt.libMSVCRT.DLL(C运行函数库)的导入函数库msvcrtd.libMSVCRTD.DLL(Debug版C运行函数库)的导入函数库libcmt.libC运行函数静态链接库(多线程)libc.libC运行函数静态链接库(单线程)MFC消息管理是MFC编程的一个重要内容,也是编写MFC应用程序的基础。MFC应用程序消息处理的方式与SDK应用程序有所不同。MFC应用程序框架截取了Windows向应用程序发出的消息,再确定将消息发送给哪一个对象,可以根据需要利用函数
39、重载对消息进行处理,但不需要处理的消息将由应用程序框架自动处理。消息管理包括消息的发送和处理。对于消息发送,MFC提供了类似于API函数功能的消息发送函数,而MFC消息处理的内部机制则相对复杂一些。从编程的角度出发,我们只需了解其大致的原理。2.3 MFC消息管理消息管理 MFC采用消消息息映映射射(Message Map)机制取代C/C+语言中的switch-case结构来处理消息。MFC消息映射机制包括一组消消息息映映射射宏宏。一条消息映射宏把一个Windows消息和其消息处理函数联结起来。MFC应用程序框架提供了消息映射功能。在类的实现源文件中用BEGIN_MESSAGE_MAP()和E
40、ND_MESSAGE_MAP()宏来定义消息映射。在类定义的结尾用DECLARE_MESSAGE_MAP()宏来声明使用消息映射。1) MFC消息映射机制消息映射机制 BEGIN_MESSAGE_MAP(theclass, baseclass)/AFX_MSG_MAP(theclass)ON_ . . . . . . /MFC预定义的消息映射宏ON_MESSAGE(message , memberFxn) /用户自定义的消息映射宏. . . . . . /AFX_MSG_MAPEND_MESSAGE_MAP()MFC应用程序应用程序MESSAGE_MAP消息映射形式:消息映射形式: 注注意意:
41、特殊注解“/AFX_MSG_MAP”是ClassWizard类向导用于维护消息映射宏的标记,用户不要删除注解轻易修改注解内的代码。启动ClassWizard,添加要求的三个消息处理函数,ClassWizard将在类的实现文件中添加三个消息映射宏和消息处理函数。消息映射宏如下:BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)/AFX_MSG_MAP(CMainFrame)ON_WM_CREATE()/由MFCAppWizard程序向导自动生成的消息映射 ON_WM_CLOSE()/由ClassWizard类向导添加 ON_WM_DESTROY() ON_COMMAN
42、D(ID_EDIT_COPY, OnEditCopy)/AFX_MSG_MAPEND_MESSAGE_MAP()例例利用ClassWizard为框架类添加消息WM_CLOSE、WM_DESTROY及菜单项“Edit|Copy”的消息处理函数,分析ClassWizard完成了哪些工作。classCMainFrame:CFrameWndpublic:CMainFrame();protected:/AFX_MSG(CMainFrame) /声明消息处理函数原形afx_msgintOnCreate(LPCREATESTRUCTlpCreateStruct);/由程序向导自动生成afx_msgvoidO
43、nClose();/由ClassWizard类向导添加afx_msgvoidOnDestroy();afx_msgvoidOnEditCopy();/AFX_MSGDECLARE_MESSAGE_MAP()/声明使用消息映射宏;ClassWizard在类的定义中声明了消息处理函数在类的定义中声明了消息处理函数 :发送消息到一个窗口可以采用传送(Send)或寄送(Post)方式,这两种方式之间的主要区别是消息被接收对象收到后是否立即被处理。Windows提供了三个API函数用于消息的发送。函数SendMessage()用于向一个或多个窗口传送消息,该函数将调用目标窗口的窗口函数,直到目标窗口处理
44、完收到的消息,该函数才返回。函数PostMessage()用于向一个或多个窗口寄送消息,它把消息放在指定窗口创建的线程的消息队列中,然后不等消息处理完就返回。2)消息的发送消息的发送函数SendDlgItemMessage()函数用于向对话框中指定的控件发送消息,直到目标控件处理完收到的消息,该函数才返回。MFC将这三个函数封装为CWnd窗口类的成员函数,封装了目标窗口句柄,它们将向调用它的窗口对象发送或寄送消息,如pMyView-SendMessage()的调用形式表示向pMyView所指对象发送消息。与用户输入相关的消息(如鼠标消息和键盘消息)通常是以寄送(Post)的方式发送,以便这些用
45、户输入可以由运行较缓慢的系统进行缓冲处理。而其它消息通常是以传送(Send)的方式发送由于程序逻辑设计结构的限制或不同窗口之间数据的同步,程序员需要手工自定义一些消息。例如,如果需要在特定时间间隔内通知所有数据输出窗口重新取得数据,就必须采用定时器事件来产生特定的消息。MFC允许用户自定义消息,常量WM_USER(为0x0400)与第一个自定义消息值相对应,用户必须为自己的消息定义相对于WM_USER的偏移值,利用#define语句定义自己的消息,例如:#defineWM_USER1WM_USER+0#defineWM_USER2WM_USER+13) 手工处理消息映射手工处理消息映射必须在函
46、数返回类型前面加上afx_msg标识,如:afx_msgLRESULTmemberFxn(WPARAM wParam , LPARAM lParam);其中,参数wParam、lParam用于传递消息的两个附加信息自定义消息处理函数的声明:自定义消息处理函数的声明:ON_MESSAGE(message,memberFxn)其中,message是消息名标识,memberFxn是对应的消息处理函数。如:ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)自定义消息映射宏的定义自定义消息映射宏的定义:1首先利用MFCAppWizardexe向导创建一个名为Rotate的应用程序。
47、利用ClassWizard类向导为CRotateView类生成消息WM_CREATE的消息处理函数,通过设置定时器在指定的时间间隔向窗口发送WM_TIMER消息。SetTimer(1,200,NULL);/启动定时器2在文件RotateView.cpp开始位置定义一个用户自定义消息:#defineWM_MYMESSAGEWM_USER+1利用ClassWizard为CRotateView类生成消息WM_TIME的消息处理函数:SendMessage(WM_MYMESSAGE); 例例编写一个自定义消息应用程序,程序启动后设置一个定时器,在WM_TIMER的消息处理函数中发送一个用户自定义消息,
48、在对应的自定义消息处理函数中以动画的形式旋转显示一行文本。3在类CRotateView的定义中声明自定义消息处理函数:afx_msg LRESULT OnMyMessage(WPARAMwParam,LPARAMlParam);在文件RotateView.cpp消息映射BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加自定义消息映射宏:ON_MESSAGE(WM_MYMESSAGE,OnMyMyessage)4在类CRotateView的定义中声明一个private属性、int型的成员变量m_dEscapement,它表示文本显示角度,并在类CRotateView的构造
49、函数中初始化:m_dEscapement=0; 在文件RotateView.cpp中手工添加自定义消息处理函数实现代码,完成以动画形式旋转显示一行文本的功能。6利用ClassWizard生成消息WM_DESTROY的消息处理函数,在销毁已创建的定时器:KillTimer(1);宏宏就是用预处理指令#define定义一个标识符,用它来表示一个字符串或一段源代码。MFC宏宏作为MFC类库的一个组成部分在MFC应用程序中经常出现。MFC宏在Afxwin.h、Afx.h及Afxmsg_.h等MFC头文件中分别进行了定义。MFC提供的宏有很多,常用的包括消息映射宏、运行时类型识别的宏、调试宏和异常宏等,
50、下表列出了一般MFC编程时要用到的宏。2.4 MFC宏宏 RUNTIME_CLASS获得运行时类的CRuntimeClass结构的DECLARE_DYNAMIC提供基本的运行时类型识别(声明)IMPLEMENT_DYNAMIC提供基本的运行时类型识别(实现)DECLARE_DYNCREATE动态创建(声明)IMPLEMENT_DYNCREATE动态创建(实现)DECLARE_SERIAL对象序列化(声明)IMPLEMENT_SERIAL对象序列化(实现)DECLARE_MESSAGE_MAP声明消息映射表BEGIN_MESSAGE_MAP开始建立消息映射表END_MESSAGE_MAP结束建立
51、消息映射表ON_COMMAND命令消息映射宏ON_MESSAGE自定义消息映射宏ON_WM_MFC预定义消息映射宏ON_BN_,ON_CBN_等控件通知(Notification)消息映射宏 运 行 时 类 型 识 别 RTTI( Run Time TypeInformation)是指在程序运行时允许确定对象的类型。MFC扩充了一般C+中运行时类型识别的功能,当一个类支持MFC的运行时类型识别时,它允许程序获取对象的信息(如类名、所占存储空间大小及版本号等)和基类信息。1. 宏宏RUNTIME_CLASS(class_name): 返回参数class_name所指定类的静态成员变量class#
52、class_name的 指 针 , 该 指 针 指 向 一 个CRuntimeClass结构。1) 有关运行时类型识别的宏有关运行时类型识别的宏2. 动动态态支支持持宏宏DECLARE_DYNAMIC()和和IMPLEMENT_DYNAMIC():使用动态支持宏能够使CObject派生类的对象具 有 基 本 的 类 型 识 别 机 能 , 可 以 通 过 调 用 成 员 函 数 CObject:IsKindOf()测试对象与给定类的关系。3. 动动态态创创建建宏宏DECLARE_DYNCREATE()和和IMPLEMENT_DYNCREATE(): 动态创建是动态支持的一个超集,除了基本的类型
53、识别机能,使用动态创建宏能够使CObject的派生类具有在运行时动态创建对象的功能。4. 序列化宏序列化宏DECLARE_SERIAL() 和和IMPLEMENT_SERIAL() /在头文件MyClass.h中classCMyClass:publicCObjectDECLARE_DYNAMIC(CMyClass)public:voidSomeFunction(void);例例定义类MyClass,使用RUNTIME_CLASS()宏的基本对象诊断功能。/在实现文件MyClass.cpp中#includeMyClass.hIMPLEMENT_DYNAMIC(CMyClass,CObject)v
54、oidCMyClass:SomeFunction(void)CObject*pObject=newCMyClass;if(pObject-IsKindOf(RUNTIME_CLASS(CMyClass)CMyClass*pMyObject=(CMyClass*)pObject;AfxMessageBox(MyObjectisanobjectoftheclassCMyClass);elseAfxMessageBox(MyObjectisnotanobjectoftheclassCMyClass);deletepObject;2) MFC调试宏调试宏跟踪声明和断言在查找程序设计错误时是非常有用的。
55、跟踪声明可以让程序在运行过程中遇到跟踪声明时在输出窗口显示指定的信息,而断言使程序在断言条件不成立时暂停程序的运行。MFC提供了一些跟踪声明和断言宏用于程序调试,这些宏只能用于DEBUG版本应用程序的调试状态。1TRACE()宏语法说明如下:TRACE ();其中参数是由输出格式和变量等组成的输出表达式,其格式与函数printf()的参数一样,它指定调试时要在Output窗口输出的内容。char*szName=“LiMing”;intnAge=18;TRACE(“Name = %s, Age = %d n”, szName, nAge ); 例例1 对于以下代码:调试时在Output窗口输出以
56、下内容:Name=LiMing,Age=18例例2 设自定义一个CFrameWnd的派生类CMyFrame,在程序中构建一个与CMyFrame相关联的文档模板对象,并为构键的文档模板创建框架窗口:CMyFrame*pFrame=(CMyFrame*)AfxGetMainWnd();ASSERT(pFrame-IsKindOf(RUNTIME_CLASS (CMyFrame); /判断pFrame的类型pFrame-DoSomeOperation();3.1 CRuntimeClass结构结构CRuntimeClass在MFC中是以结构的方式定义,它含有使用CRuntimeClass结构的类的有
57、关信息,与CObject类一同实现运行时类型识别RTTI的功能。CRuntimeClass结构包含了类名、对象所占存储空间大小及类的版本号等成员变量和动态对象创建、派生关系判断等成员函数。每个从CObject类派生的类都有一个CRuntimeClass对象同它关联。要使用CRuntimeClass结构,必须结合使用RUNTIME_CLASS()宏和其它有关运行时类型识别的MFC宏。3 常用的常用的MFC类类 3.2 CObject类类由于MFC中大部分类是从CObject类继承而来的,CObject类描述了几乎所有的MFC类的一些公共特性,CObject类为程序员提供了对象诊断、运行时类型识别
58、和序列化等功能。对象诊断。MFC提供了两种诊断特性:利用成员函数AssertValid()进行对象有效性检查,使得类可以在继续运行前对自己进行正确性检查;利用成员函数Dump()输出对象的数据成员的值。运行时访问类的信息:提供GetRuntimeClass()和IskindOf()两个成员函数来支持运行时类型识别。对象序列化。voidCHuman:AssertValid()constCObject:AssertValid();ASSERT(m_year0);ASSERT(m_year120);例例1 通过“Insert|NewClass”定义一个CObject的派生类CHuman。在类CHum
59、an中重载AssertValid()函数,利用ASSERT宏把人的年龄限制在一个合理的范围。voidCAssertView:OnDraw(CDC*pDC)CHumanman;man.m_year=200;man.AssertValid();/超出年龄范围,将断言失败voidCHuman:Dump(CDumpContext&dc)constCObject:Dump(dc);dcAge=m_yearm_year=39;#ifdef_DEBUG/调试时在调试器输出窗口显示m_year的值pMyPerson-Dump(afxDump);#endif3.3 CCmdTarget类类CCmdTarget类
60、由CObject类直接派生而来,它负责将消息发送到能够响应这些消息的对象。它是所有能进行消息映射的MFC类的基类。设置光标。CCmdTarget类定义3个函数用于改变光标状态:BeginWaitCursor()将光标改为沙漏形状,EndWaitCursor()将光标改回调用BeginWait-Cursor()之前的形状,RestoreWaitCursor()用于将光标还原为等待状态。voidCMyView:OnSomeCommand()BeginWaitCursor(); /显示沙漏状光标./进行某种操作EndWaitCursor();/恢复原来光标的形状3.4 CWinApp类类在MFC应用
61、程序中,CWinApp类取代了WinMain()主函数在SDK应用程序中的地位。传统SDK应用程序WinMain()函数完成的工作现在由类CWinApp的InitApplication()、InitInstance()和Run()三个成员函数承担。在任何MFC应用程序中有且仅有一个CWinApp派生类的对象,它代表了程序中运行的主线程,也代表了应用程序本身。3.5 CWnd类类CWnd类由CCmdTarget类直接派生而来,该类及其派生类的实例是一个窗口。CWnd类代表了MFC中最基本的GUI对象,它是一个功能最完善、成员函数最多的MFC类。窗口的实例包括应用程序主窗口、对话框和控件等。CWn
62、d类提供的功能包括注册新窗口类、创建窗口及子窗口、获取窗口、管理窗口、访问窗口及控件、控制窗口光标、创建和使用句柄和支持工具提示等,3.6 CFrameWnd类类CFrameWnd类是CWnd类的派生类,主要用来掌管一个窗口,它取代了SDK应用程序中窗口函数WndProc()的地位。CFrameWnd类的对象是一个框架窗口,包括边框、标题栏、菜单、最大化按钮、最小化按钮和一个激活的视图。CFrameWnd支持SDI界面,对于MDI界面,使用其两个派生类CMDIFrameWnd和CMDIChildWnd。CFrameWnd提供了若干个成员函数用于获得和设置活动文档、视图、图文框、标题栏、状态栏等操作。3.7 CDocument和和CView类类CDocument类在应用程序中作为用户文档类的基类,它代表了用户存储或打开的一个文件。CDocument类的主要功能是把对数据的处理从对用户的界面处理中分离出来,同时提供了一个与视图类交互的接口。CDocument类支持标准的文件操作,如创建、打开和存储一个文档等。CView类是MFC中一个很基本的类,它作为其它MFC视图类和用户视图派生类的基类。