1 / 55

第 6 章 MFC 原理与方法

第 6 章 MFC 原理与方法. 一个优秀的编程工具往往匹配一个功能强大的类库,类库封装了大量 Windows 编程需要使用的函数和数据结构。与 Visual C++ 捆绑在一起的 MFC (Microsoft Foundation Class) 微软基础类 就是这样一个由 Microsoft 公司设计的类库。采用 MFC 方式编程提高了 Windows 应用程序的开发效率,但由于 MFC 应用程序结构的复杂性和透明性,要想完全掌握 MFC 应用程序的内部机制需要付出很大的努力。

lois
Télécharger la présentation

第 6 章 MFC 原理与方法

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第 6 章 MFC原理与方法

  2. 一个优秀的编程工具往往匹配一个功能强大的类库,类库封装了大量Windows编程需要使用的函数和数据结构。与Visual C++捆绑在一起的MFC(Microsoft Foundation Class)微软基础类就是这样一个由Microsoft公司设计的类库。采用MFC方式编程提高了Windows应用程序的开发效率,但由于MFC应用程序结构的复杂性和透明性,要想完全掌握MFC应用程序的内部机制需要付出很大的努力。 认识MFC只是一个过程、一个手段,最终目的是为了良好并熟练地运用MFC。

  3. 本章主要学习内容: • Windows的编程机制 • MFC的基本原理和使用方法: • MFC类 • MFC应用程序框架 • MFC消息管理 • MFC宏 • 常用的MFC类

  4. 6.1 Windows编程机制 • Windows是一个多进程的图形窗口操作系统,Windows应用程序与DOS应用程序有很大的区别。DOS应用程序采用顺序执行过程,而Windows是一个基于事件的消息(Message)驱动系统。 • Windows应用程序是按照“事件→消息→处理”非顺序的机制运行。当有某个事件(如单击鼠标、键盘输入和执行菜单命令等)发生时,Windows会根据具体的事件产生对应的消息,并发送到指定应用程序的消息队列;应用程序从消息队列中取出消息,并根据不同的消息进行不同的处理。

  5. 6.1.1 关于API和SDK • Windows API(Application Programming Interface)是Windows操作系统与应用程序之间的标准接口,它提供了上千个标准函数、宏和数据结构的定义。 • 在使用Visual C++、Visual Basic和Delphi编程时都可以调用Windows API函数,Windows应用程序可通过调用标准Windows API函数使用系统提供的功能。 • Windows API函数定义在一些DLL动态链接库中,其中最主要的DLL是User32.dll、Gdi32.dll和Kernel32.dll三个库文件。

  6. 传统SDK编程:程序员通过调用API函数,自己动手、按部就班地实现程序各部分的功能。SDK应用程序的结构比较清晰,但程序员必须编写所有的功能代码。传统SDK编程:程序员通过调用API函数,自己动手、按部就班地实现程序各部分的功能。SDK应用程序的结构比较清晰,但程序员必须编写所有的功能代码。 • 利用Visual C++编写一个类SDK应用程序:首先利用Win32 Application向导建立一个Windows应用程序框架,然后根据需要可以向程序项目中添加一些头文件、实现源文件和资源文件,并编写具体的程序代码。 例编写一个名为Hello的类SDK应用程序,当单击鼠标时通过调用API函数,以弹出一个提示信息对话框。

  7. 1.执行“File|New”菜单命令,在New对话框的Project页面中选择Win32 Application项目类型,输入程序名Hello。在向导第1步选择“A typital “Hello world!” application”项,单击Finish按钮。 2.在窗口函数WndProc()的消息处理分支switch-case结构中添加WM_LBUTTONDOWN鼠标单击消息及其处理代码: case WM_LBUTTONDOWN: MessageBox(NULL, "You pressed the left button of mouse !","Message",NULL); break;

  8. SDK应用程序结构: • 一个由API函数构造的Windows程序的功能由三个部分组成:入口函数、窗口函数和Windows系统。 • 每一个程序都有一个主函数,WinMain()函数就是Windows程序的入口主函数。该函数的主要任务是完成一些初始化工作并维护一个消息循环。当消息循环结束后,就退出了WinMain()函数,也就退出了应用程序。此外,WinMain()函数还负责完成窗口的注册、创建和显示。 • Windows程序以窗口的形式存在,在不同窗口之间传递消息是Windows和应用程序进行交流的主要形式。程序具体功能由不同的窗口函数实现。

  9. 6.1.2 句柄 • 在Windows中,用句柄(Handle)标识应用程序中不同的对象和同类对象中不同的实例,如一个具体的窗口、按钮、输出设备、画笔和文件等。通过句柄可以获得相应的对象信息。常用的句柄类型有: HWND 、 HINSTANCE 、HDC 、HCURSOR、HICON、HMENU等。 • 句柄常作为Windows消息和API函数的参数,在采用API方法编写Windows应用程序时要经常使用句柄。而采用MFC方法编写Windows应用程序时,由于对应的MFC类已对句柄进行了封装,大多数情况下不再需要访问句柄。

  10. 6.1.3 事件和消息 • 所谓消息就是用于描述某个事件发生的信息,而事件是对于Windows的某种操作。 • 事件和消息密切相关,事件是因,消息是果,事件产生消息,消息对应事件。所谓消息的响应,其实质就是事件的响应。 • 消息驱动是Windows应用程序的核心,所有的外部响应(如键盘、鼠标和计时器等)都被Windows先拦截,转换成消息后再发送到应用程序中的目标对象,应用程序根据消息的具体内容进行处理。 • 消息不仅可由Windows发出,它也可由应用程序本身或其它程序产生。Windows为每一个应用程序都维护一个或多个消息队列,发送到每个程序窗口的消息都排成一个队列。

  11. 消息队列和在应用程序中的轮询处理

  12. Windows消息分为三种类型: • 标准Windows消息:以WM_前缀(但不包括WM_COMMAND)开始的消息,包括鼠标消息、键盘消息和窗口消息,如WM_MOVE 、WM_PAINT等。 • 控件通知(Control Notification)消息:对控件操作引起的消息,是控件和子窗口向其父窗口发出的WM_COMMAND通知消息。例如,当用户修改了编辑控件中的文本后,编辑控件向其父窗口发送WM_COMMAND通知消息。 • 命令(Command)消息:由菜单项、工具栏按钮、快捷键等用户界面对象发出的WM_COMMAND消息。命令消息与其它消息不同,它可被更广泛的的对象如文档、文档模板、应用程序对象、窗口和视图等处理。

  13. 消息用MSG结构表示: typedef struct tagMSG { HWND hWnd; // 目标窗口句柄 UINT message; // 消息标识 WPARAM wParam; // 消息参数1(附加信息,16位) LPARAM lParam; // 消息参数2(附加信息,32位) DWORD time; // 消息发送时间 POINT pt; // 消息发送时鼠标的屏幕坐标 } MSG;

  14. 为程序Hello添加键盘消息处理功能,判断当前按下的键是不是A或a键,并给出相应的提示。 打开程序项目Hello,在文件Hello.cpp的窗口函数WndProc()的switch消息处理分支中添加键盘消息的处理代码: case WM_KEYDOWN: // 处理键盘消息 if(wParam==0x41) // A或a键的虚键码为0x41H MessageBox(NULL, "The key you pressed is A or a !","KEYDOWN",NULL); else MessageBox(NULL, "The key you pressed is not A or a !","KEYDOWN",NULL); break;

  15. 6.2 MFC微软基础类 利用Windows API开发程序的用户有这样的体会,即使开发一个简单的Windows应用程序也需要对Windows的编程原理有很深刻的认识,需要手工编写冗长的代码。由于程序的出错率是随着代码长度的增加呈几何级数增长的,而且当程序长度逐渐膨胀时,调试程序会变得越来越困难。因此,传统的Windows应用程序设计需要程序员有极大的耐心和丰富的编程经验。 Visual C++捆绑了微软的基础类MFC,编程时我们就可以利用类的可重用性和可扩充性,大大降低Windows应用程序设计的难度和工作量。

  16. 6.2.1 MFC概述 类库是一个可以在应用程序中使用的相互关联的C++类的集合。 MFC作为一个Windows编程类库,它包含了200多个类,封装了Windows的大部分编程对象以及与它们有关的操作。 虽然程序在功能上千差万别,但从本质上看,都可以分为用户界面设计、文件操作、数据库访问及多媒体使用等几部分,这些都可以通过一些类来实现。MFC提供了一个标准化的程序结构,使开发人员不必从头设计一个Windows应用程序。 MFC实际上是一个庞大的文件库,它由几百个执行文件和源代码文件(如H文件)组成。

  17. 采用MFC编程的优点: 使用标准化的程序代码结构,有利于程序员之间的交流。 Visual C++为MFC提供了大量的工具支持,提高了编程效率。如利用MFC AppWizard创建MFC应用程序框架,利用ClassWizard方便地对Windows消息进行管理。 MFC应用程序的效率较高,只比传统的Windows C程序低5%左右。并且,在MFC应用程序中还允许混合使用传统的Windows API函数。 其它优势:完全支持Windows所有的函数、控件、消息、菜单及对话框;具有良好的稳定性和可移植性,更符合微软的风格等。

  18. 6.2.2 MFC体系结构 MFC主要组成部分:类、宏和全局函数。 类是MFC中最主要的内容。MFC类是以层次结构方式组织起来的。MFC中的类分成两部分,除了一些辅助类,大多数的MFC类是直接或间接从根类CObject派生而来。 几乎每一个派生层次都与一具体的Windows实例相对应,如文档类、窗口类和视图类等。 MFC宏主要功能:消息映射、运行时对象类型服务、诊断服务、异常处理。 MFC约定:全局函数以“Afx”为前缀,全局变量以“afx”为前缀。

  19. 6.2.3 学习MFC的方法 首先要对Windows编程概念和API函数有一定的了解,如Windows API有哪些功能和哪些常用的数据结构等。 学会抽象地把握问题,不求甚解,不要一开始学习Visual C++就试图了解整个MFC类库。从理解和使用两个方面学习MFC,理解MFC应用程序的框架结构。 先大体上了解MFC的概念、组成和基本约定,从简单的类入手,结合程序设计,由浅入深,循序渐进、日积月累。 编程时如果MFC某个类能完成所需要的功能,可以直接调用已有类的方法(成员函数)。否则,可以利用面向对象技术中的“继承”方法对MFC类的行为进行扩充和修改,从MFC中已有的类派生出自己需要的类。 学习MFC,另一点就是不要过分依赖于向导(Wizard)工具。向导能做许多工作,但同时掩饰了太多的细节。

  20. 6.3 MFC应用程序框架 尽管每个应用程序具体实现的功能不同,但同一类程序的基本结构是相同的。因此,通常采用MFC AppWizard创建一个MFC应用程序框架。 MFC不仅仅是一个类库,它还提供了一层建立在MFC类对象封装上的附加应用程序框架。应用程序框架是为了生成一般的应用程序所必需的各种软组件的集成,是类库的一种超集。 类库只是一种可以嵌入到任何程序中的、提供某些特定功能的类的集合。而应用程序框架却定制了应用程序的结构和源代码,其中的类对象既相互独立、又相互作用,形成一个统一的整体。

  21. 6.3.1 应用程序框架中的对象 MFC应用程序框架提供了构建应用程序所需要的类,在程序运行时能够生成运行时类的对象,如代表应用程序对象、文档对象、视图对象和框架窗口对象。应用程序对象theApp是一个唯一的全局变量,它的主要功能是通过调用WinMain()主函数启动程序的运行。 MFC应用程序框架也有一个作为程序入口点的WinMain()主函数,但在源程序中看不见该函数,它在MFC中已定义好并同应用程序相链接。

  22. MFC应用程序对象之间的关系

  23. 6.3.2 MFC应用程序的生与死 在MFC应用程序的CWinApp派生类对象theApp是一个全局变量,代表了应用程序运行的主线程。它在程序整个运行期间都存在,它的销毁意味着运行程序的消亡。 MFC应用程序启动时,首先创建应用程序对象theApp,这时将自动调用应用程序类的构造函数初始化对象theApp,然后由应用程序框架调用MFC提供的AfxWinMain()主函数。 AfxWinMain()主函数首先通过调用全局函数AfxGetApp()获取应用程序对象theApp的指针pApp,然后通过pApp调用应用程序对象的有关成员函数,完成程序的初始化和启动工作,最后调用成员函数Run(),进入消息循环。 程序运行后将收到WM_PAINT消息,调用OnPaint()函数绘制客户区窗口。如果Run()收到WM_QUIT消息,则结束消息循环,然后调用函数ExitInstance(),结束程序运行。

  24. MFC应用程序运行后各函数的调用关系 InitInstance()函数是派生类唯一需要重载的函数,它负责应用程序的初始化,如初始化数据、创建文档模板、处理命令行以及显示应用程序主窗口。

  25. 文 件 名 称 说 明 afxwin.h 声明MFC核心类 afxext.h MFC扩展文件,声明工具栏、状态栏、拆分窗口等类 afxdisp.h 声明OLE类 afxdtctl.h 声明支持IE 4公用控件的MFC类,如CImageList等 afxcmn.h 声明Windows公共控件类 Mfc42.lib MFCxx.DLL的导入函数库(Release版) Mfc42D.lib MFCxx.DLL的导入函数库(Debug版) MfcS42.lib MFCSxx.DLL的导入函数库(Static Release版) MfcS42D.lib MFCSxxD.DLL的导入函数库(Static Debug版) Mfc42U.lib MFCxxU.DLL的导入函数库(Unicode Release版) 6.3.3 常用的MFC文件和库文件

  26. Mfc42UD.lib MFCxxUD.DLL的导入函数库(Unicode Debug版) MfcO42D.lib MFCOxxD.DLL的导入函数库(OLE Debug版) MfcD42D.lib MFCDxxD.DLL的导入函数库(Database Debug版) Nafxcw.lib MFC静态链接库(Release版) NafxcwD.lib MFC静态链接库(Debug版) gdi32.lib GDI32.DLL的导入函数库 user32.lib USER32.DLL的导入函数库 kernel32.lib KERNEL32.DLL的导入函数库 msvcrt.lib MSVCRT.DLL(C运行函数库)的导入函数库 msvcrtd.lib MSVCRTD.DLL(Debug版C运行函数库)的导入函数库 libcmt.lib C运行函数静态链接库(多线程) libc.lib C运行函数静态链接库(单线程)

  27. 6.4 MFC消息管理 MFC消息管理是MFC编程的一个重要内容,也是编写MFC应用程序的基础。 MFC应用程序消息处理的方式与SDK应用程序有所不同。MFC应用程序框架截取了Windows向应用程序发出的消息,再确定将消息发送给哪一个对象,可以根据需要利用函数重载对消息进行处理,但不需要处理的消息将由应用程序框架自动处理。 消息管理包括消息的发送和处理。对于消息发送,MFC提供了类似于API函数功能的消息发送函数,而MFC消息处理的内部机制则相对复杂一些。从编程的角度出发,我们只需了解其大致的原理。

  28. 6.4.1 MFC消息映射机制 MFC采用消息映射(Message Map)机制取代C/C++语言中的switch-case结构来处理消息。 MFC消息映射机制包括一组消息映射宏。一条消息映射宏把一个Windows消息和其消息处理函数联结起来。 MFC应用程序框架提供了消息映射功能。 在类的实现源文件中用BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏来定义消息映射。 在类定义的结尾用DECLARE_MESSAGE_MAP()宏来声明使用消息映射。

  29. MFC应用程序MESSAGE_MAP消息映射形式: 注意:特殊注解“//{{AFX_MSG_MAP”是ClassWizard类向导用于维护消息映射宏的标记,用户不要删除注解轻易修改注解内的代码。 BEGIN_MESSAGE_MAP(theclass, baseclass) //{{AFX_MSG_MAP(theclass) ON_ . . . . . . // MFC预定义的消息映射宏 ON_MESSAGE(message , memberFxn) // 用户自定义的消息映射宏 . . . . . . //}}AFX_MSG_MAP END_MESSAGE_MAP()

  30. 例利用ClassWizard为框架类添加消息WM_CLOSE、WM_DESTROY及菜单项“Edit|Copy”的消息处理函数,分析ClassWizard完成了哪些工作。例利用ClassWizard为框架类添加消息WM_CLOSE、WM_DESTROY及菜单项“Edit|Copy”的消息处理函数,分析ClassWizard完成了哪些工作。 启动ClassWizard,添加要求的三个消息处理函数,ClassWizard将在类的实现文件中添加三个消息映射宏和消息处理函数。消息映射宏如下: BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_WM_CREATE() // 由MFC AppWizard程序向导自动生成的消息映射 ON_WM_CLOSE() // 由ClassWizard类向导添加 ON_WM_DESTROY() ON_COMMAND(ID_EDIT_COPY, OnEditCopy) //}}AFX_MSG_MAP END_MESSAGE_MAP()

  31. ClassWizard在类的定义中声明了消息处理函数 : class CMainFrame : CFrameWnd { public: CMainFrame(); protected: //{{AFX_MSG(CMainFrame) // 声明消息处理函数原形 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // 由程序向导自动生成 afx_msg void OnClose(); // 由ClassWizard类向导添加 afx_msg void OnDestroy(); afx_msg void OnEditCopy(); //}}AFX_MSG DECLARE_MESSAGE_MAP() // 声明使用消息映射宏 };

  32. 6.4.2 消息的发送 发送消息到一个窗口可以采用传送(Send)或寄送(Post)方式,这两种方式之间的主要区别是消息被接收对象收到后是否立即被处理。Windows提供了三个API函数用于消息的发送。 函数SendMessage()用于向一个或多个窗口传送消息,该函数将调用目标窗口的窗口函数,直到目标窗口处理完收到的消息,该函数才返回。 函数PostMessage()用于向一个或多个窗口寄送消息,它把消息放在指定窗口创建的线程的消息队列中,然后不等消息处理完就返回。

  33. 函数SendDlgItemMessage()函数用于向对话框中指定的控件发送消息,直到目标控件处理完收到的消息,该函数才返回。函数SendDlgItemMessage()函数用于向对话框中指定的控件发送消息,直到目标控件处理完收到的消息,该函数才返回。 MFC将这三个函数封装为CWnd窗口类的成员函数,封装了目标窗口句柄,它们将向调用它的窗口对象发送或寄送消息,如pMyView->SendMessage()的调用形式表示向pMyView所指对象发送消息。 与用户输入相关的消息(如鼠标消息和键盘消息)通常是以寄送(Post)的方式发送,以便这些用户输入可以由运行较缓慢的系统进行缓冲处理。而其它消息通常是以传送(Send)的方式发送

  34. 6.4.3 手工处理消息映射 由于程序逻辑设计结构的限制或不同窗口之间数据的同步,程序员需要手工自定义一些消息。例如,如果需要在特定时间间隔内通知所有数据输出窗口重新取得数据,就必须采用定时器事件来产生特定的消息。 MFC允许用户自定义消息,常量WM_USER(为0x0400)与第一个自定义消息值相对应,用户必须为自己的消息定义相对于WM_USER的偏移值,。利用#define语句定义自己的消息,例如: #define WM_USER1 WM_USER+0 #define WM_USER2 WM_USER+1

  35. 自定义消息处理函数的声明: 自定义消息映射宏的定义: ON_MESSAGE(message, memberFxn) 其中,message是消息名标识,memberFxn是对应的 消息处理函数。如: ON_MESSAGE(WM_MYMESSAGE, OnMyMessage) 必须在函数返回类型前面加上afx_msg标识,如: afx_msg LRESULT memberFxn ( WPARAM wParam , LPARAM lParam); 其中,参数wParam、lParam用于传递消息的两个附加信息

  36. 例编写一个自定义消息应用程序,程序启动后设置一个定时器,在WM_TIMER的消息处理函数中发送一个用户自定义消息,在对应的自定义消息处理函数中以动画的形式旋转显示一行文本。例编写一个自定义消息应用程序,程序启动后设置一个定时器,在WM_TIMER的消息处理函数中发送一个用户自定义消息,在对应的自定义消息处理函数中以动画的形式旋转显示一行文本。 1.首先利用MFC AppWizard[exe]向导创建一个名为Rotate的应用程序。利用ClassWizard类向导为CRotateView类生成消息WM_CREATE的消息处理函数,通过设置定时器在指定的时间间隔向窗口发送WM_TIMER消息。 SetTimer(1,200,NULL); // 启动定时器 2.在文件RotateView.cpp开始位置定义一个用户自定义消息: #define WM_MYMESSAGE WM_USER+1 利用ClassWizard为CRotateView类生成消息WM_TIME的消息处理函数:SendMessage(WM_MYMESSAGE);

  37. 3.在类CRotateView的定义中声明自定义消息处理函数:3.在类CRotateView的定义中声明自定义消息处理函数: afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); 在文件RotateView.cpp消息映射BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加自定义消息映射宏: ON_MESSAGE(WM_MYMESSAGE,OnMyMyessage) 4.在类CRotateView的定义中声明一个private属性、int型的成员变量m_dEscapement,它表示文本显示角度,并在类CRotateView的构造函数中初始化:m_dEscapement=0; 5.在文件RotateView.cpp中手工添加自定义消息处理函数实现代码,完成以动画形式旋转显示一行文本的功能。 6.利用ClassWizard生成消息WM_DESTROY的消息处理函数,在销毁已创建的定时器:KillTimer(1);

  38. 6.5 MFC宏 宏就是用预处理指令#define定义一个标识符,用它来表示一个字符串或一段源代码。MFC宏作为MFC类库的一个组成部分在MFC应用程序中经常出现。MFC宏在Afxwin.h、Afx.h及Afxmsg_.h等MFC头文件中分别进行了定义。 MFC提供的宏有很多,常用的包括消息映射宏、运行时类型识别的宏、调试宏和异常宏等,下表列出了一般MFC编程时要用到的宏。

  39. RUNTIME_CLASS 获得运行时类的CRuntimeClass结构的 DECLARE_DYNAMIC 提供基本的运行时类型识别(声明) IMPLEMENT_ DYNAMIC 提供基本的运行时类型识别(实现) DECLARE_DYNCREATE 动态创建(声明) IMPLEMENT_DYNCREATE 动态创建(实现) DECLARE_SERIAL 对象序列化(声明) IMPLEMENT_SERIAL 对象序列化(实现) DECLARE_MESSAGE_MAP 声明消息映射表 BEGIN_MESSAGE_MAP 开始建立消息映射表 END_MESSAGE_MAP 结束建立消息映射表 ON_COMMAND 命令消息映射宏 ON_MESSAGE 自定义消息映射宏 ON_WM_… MFC预定义消息映射宏 ON_BN_… , ON_CBN_… 等 控件通知(Notification)消息映射宏

  40. 6.5.1 有关运行时类型识别的宏 运行时类型识别RTTI(Run Time Type Information)是指在程序运行时允许确定对象的类型。MFC扩充了一般C++中运行时类型识别的功能,当一个类支持MFC的运行时类型识别时,它允许程序获取对象的信息(如类名、所占存储空间大小及版本号等)和基类信息。 1. 宏RUNTIME_CLASS(class_name): 返回参数class_name所指定类的静态成员变量class##class_name的指针,该指针指向一个CRuntimeClass结构。

  41. 2. 动态支持宏DECLARE_DYNAMIC()和IMPLEMENT_DYNAMIC(): 使用动态支持宏能够使CObject派生类的对象具有基本的类型识别机能,可以通过调用成员函数CObject::IsKindOf()测试对象与给定类的关系。 3. 动态创建宏DECLARE_DYNCREATE()和IMPLEMENT_DYNCREATE(): 动态创建是动态支持的一个超集,除了基本的类型识别机能,使用动态创建宏能够使CObject的派生类具有在运行时动态创建对象的功能。 4. 序列化宏DECLARE_SERIAL() 和IMPLEMENT_SERIAL()

  42. 例定义类MyClass,使用RUNTIME_CLASS()宏的基本对象诊断功能。例定义类MyClass,使用RUNTIME_CLASS()宏的基本对象诊断功能。 // 在头文件MyClass.h中 class CMyClass : public CObject { DECLARE_DYNAMIC(CMyClass) public: void SomeFunction(void); };

  43. // 在实现文件MyClass.cpp中 #include "MyClass.h" IMPLEMENT_DYNAMIC(CMyClass, CObject) void CMyClass::SomeFunction(void) { CObject* pObject=new CMyClass; if(pObject->IsKindOf(RUNTIME_CLASS(CMyClass))) { CMyClass* pMyObject=(CMyClass*) pObject; AfxMessageBox("MyObject is an object of the class CMyClass"); } else AfxMessageBox("MyObject is not an object of the class CMyClass"); delete pObject; }

  44. 6.5.2 MFC调试宏 跟踪声明和断言在查找程序设计错误时是非常有用的。跟踪声明可以让程序在运行过程中遇到跟踪声明时在输出窗口显示指定的信息,而断言使程序在断言条件不成立时暂停程序的运行。MFC提供了一些跟踪声明和断言宏用于程序调试,这些宏只能用于DEBUG版本应用程序的调试状态。 1.TRACE()宏语法说明如下: TRACE (<表达式>); 其中参数<表达式>是由输出格式和变量等组成的输出表达式,其格式与函数printf()的参数一样,它指定调试时要在Output窗口输出的内容。

  45. 例1对于以下代码: 调试时在Output窗口输出以下内容: Name = LiMing, Age = 18 例2 设自定义一个CFrameWnd的派生类CMyFrame,在程序中构建一个与CMyFrame相关联的文档模板对象,并为构键的文档模板创建框架窗口: CMyFrame* pFrame=(CMyFrame*) AfxGetMainWnd(); ASSERT(pFrame->IsKindOf(RUNTIME_CLASS (CMyFrame))); // 判断pFrame的类型 pFrame->DoSomeOperation(); char* szName = “LiMing”; int nAge = 18; TRACE(“Name = %s, Age = %d \n”, szName, nAge );

  46. 6.6 常用的MFC类 6.6.1 CRuntimeClass结构 CRuntimeClass在MFC中是以结构的方式定义,它含有使用CRuntimeClass结构的类的有关信息,与CObject类一同实现运行时类型识别RTTI的功能。CRuntimeClass结构包含了类名、对象所占存储空间大小及类的版本号等成员变量和动态对象创建、派生关系判断等成员函数。 每个从CObject类派生的类都有一个CRuntimeClass对象同它关联。要使用CRuntimeClass结构,必须结合使用RUNTIME_CLASS()宏和其它有关运行时类型识别的MFC宏。

  47. 6.6.2 CObject类 由于MFC中大部分类是从CObject类继承而来的,CObject类描述了几乎所有的MFC类的一些公共特性,CObject类为程序员提供了对象诊断、运行时类型识别和序列化等功能。 对象诊断。MFC提供了两种诊断特性:利用成员函数AssertValid()进行对象有效性检查,使得类可以在继续运行前对自己进行正确性检查;利用成员函数Dump()输出对象的数据成员的值。 运行时访问类的信息:提供GetRuntimeClass()和IskindOf()两个成员函数来支持运行时类型识别。 对象序列化。

  48. 例1通过“Insert|New Class”定义一个CObject的派生类CHuman。在类CHuman中重载AssertValid()函数,利用ASSERT宏把人的年龄限制在一个合理的范围。 void CAssertView::OnDraw(CDC* pDC) { CHuman man; man.m_year=200; man.AssertValid(); // 超出年龄范围,将断言失败 } void CHuman::AssertValid() const { CObject::AssertValid(); ASSERT(m_year>0); ASSERT(m_year<120); }

  49. 例2在派生类CHuman中重载成员函数Dump(),输出数据成员m_year的值。 调用Dump()函数: CHuman* pMyPerson=new CHuman; pMyPerson->m_year=39; #ifdef _DEBUG // 调试时在调试器输出窗口显示m_year的值 pMyPerson->Dump(afxDump); #endif void CHuman::Dump(CDumpContext &dc ) const { CObject::Dump(dc); dc<<"Age = "<<m_year<<"\n"; }

  50. void CMyView::OnSomeCommand() { BeginWaitCursor(); // 显示沙漏状光标 . . . . . . // 进行某种操作 EndWaitCursor(); // 恢复原来光标的形状 } 6.6.3 CCmdTarget类 CCmdTarget类由CObject类直接派生而来,它负责将消息发送到能够响应这些消息的对象。它是所有能进行消息映射的MFC类的基类。 设置光标。CCmdTarget类定义3个函数用于改变光标状态:BeginWaitCursor()将光标改为沙漏形状,EndWaitCursor()将光标改回调用BeginWait- Cursor()之前的形状,RestoreWaitCursor()用于将光标还原为等待状态。

More Related