1、第8章 MFC编程基础,8.1 MFC类库简介 8.2 使用AppWizard开发MFC应用程序 8.3 MFC应用程序框架与Win32程序的关联 8.4 MFC应用程序的消息映射 习 题,8.1 MFC类库简介,下面是MFC类库的类定义文件afxwin .h中的类说明部分源代码,从中我们可以了解MFC类库中都有哪些类以及它们的层次关系。代码如下:class CSize;class CPoint;class CRect;/CObject/CException,/CSimpleExceptionclass CResourceException;/ Win resource failure exc
2、eptionclass CUserException; / Message Box alert and stop operationclass CGdiObject; / CDC drawing toolclass CPen; / a pen / HPEN wrapperclass CBrush; / a brush / HBRUSH wrapperclass CFont; / a font / HFONT wrapperclass CBitmap; / a bitmap / HBITMAP wrapperclass CPalette; / a palette / HPALLETE wrapp
3、erclass CRgn; / a region / HRGN wrapper,class CDC; / a Display Context / HDC wrapperclass CClientDC; / CDC for client of windowclass CWindowDC; / CDC for entire windowclass CPaintDC; / embeddable BeginPaint struct helperclass CMenu; / a menu / HMENU wrapperclass CCmdTarget; / a target for user comma
4、ndsclass CWnd; / a window / HWND wrapper,class CDialog; / a dialog/ standard windows controlsclass CStatic; / Static controlclass CButton; / Button controlclass CListBox; / ListBox controlclass CCheckListBox; / special listbox with checksclass CComboBox; / ComboBox controlclass CEdit; / Edit control
5、class CScrollBar; / ScrollBar control,/ frame windowsclass CFrameWnd; / standard SDI frameclass CMDIFrameWnd; / standard MDI frameclass CMDIChildWnd; / standard MDI childclass CMiniFrameWnd; / half-height caption frame wnd,/ views on a documentclass CView; / a view on a documentclass CScrollView; /
6、a scrolling viewclass CWinThread; / thread base classclass CWinApp; / application base classclass CDocTemplate; / template for document creationclass CSingleDocTemplate; / SDI supportclass CMultiDocTemplate; / MDI supportclass CDocument; / main document abstraction,/ Helper classesclass CCmdUI; / Me
7、nu/button enablingclass CDataExchange; / Data exchange and validation contextclass CCommandLineInfo; /CommandLine parsing helperclass CDocManager; / CDocTemplate manager object,下面按功能对MFC库中的常用类做一简要介绍。1根类CObjectMicrosoft基本类库中的大多数类都是由CObject类派生而来的。CObject对所有由它派生出的类提供了有用的基本服务。2MFC应用结构类MFC应用结构类用于构造一个应用的框
8、架,对大多数应用提供了通用功能。我们可以在框架中填写对于应用特定的功能。,AppWizard可以产生几种类型的应用,所有这些都以不同的方式使用应用框架。1) 应用程序和线程支持类2) 命令例程类3) 文档类4) 文档模板类,3窗口、对话框和控件类1) CWnd类2) 框架窗口类3) 对话框类4) 视图类5) 控件类6) 控件条类,4菜单类5绘图和打印类6简单的数据类型类7数组、列表和映射类8文件和数据库类1) 文件I/O类2) DAO类3) ODBC类,9Internet和网络类Internet和网络类允许与其他使用ISAPI的计算机或一个Windows Socket交换信息。10OLE类11
9、调试和异常类,8.2 使用AppWizard开发MFC应用程序,8.2.1 生成MFC应用程序框架例8-1 使用MFC AppWizard生成一个简单的MFC应用程序。建立MFC应用程序框架一般可以通过以下步骤来实现:(1) 启动MFC AppWizard:定义项目名称。在File菜单下选择New,出现如图8-l所示的New对话框。,图8-1 New对话框,在图8-1中选择MFC AppWizardexe,在Project name文本输入框中输入新建的项目名如Mymfc后,单击OK按钮,将弹出MFC AppWizard-Step 1对话框,如图8-2所示。,图8-2 MFC AppWizar
10、d-Step 1对话框,(2) MFC AppWizard-Step 1对话框:选择应用程序的类型和语言。在设置好上述选项后,单击Next按钮,将弹出MFC AppWizard-Step 2 of 6对话框,如图8-3所示。,图8-3 MFC AppWizard-Step 2 of 6对话框,(3) MFC AppWizard-Step 2 of 6对话框:设置数据库选项。在该对话框中可以设置应用程序所包含的如下特点:None:默认选项,不使用数据库特征;Header files only:标题文件;Database view without file support:数据库查看不使用文件支持
11、;Database view with file support:数据库查看使用文件支持。,选择默认选项None后,单击Next按钮,将弹出MFC AppWizard-Step 3 of 6对话框,如图8-4所示。,图8-4 MFC AppWizard-Step 3 of 6对话框,(4) MFC AppWizard-Step 3 of 6对话框:设置OLE选项。因为这里生成的Mymfc应用程序不使用OLE,所以选择默认选项None,单击Next按钮,将弹出MFC AppWizard-Step 4 of 6对话框,如图8-5所示。,图8-5 MFC AppWizard-Step 4 of 6对
12、话框,(5) MFC AppWizard-Step 4 of 6对话框设置应用程序外观。在该对话框下设置应用程序的外观,它们包括:l Docking toolbar:工具栏;l Initial status bar:状态栏;l Printing and print preview:打印和打印预览;l Context-sensitive Help:上下文相关帮助;l 3D controle:3D控件;l MAPI Messaging API:MAPI电子邮件;l Windows Sockets:Windows套接字。,设置工具栏的风格:l Normal:普通的工具栏;l Internet Exp
13、lorer ReBars:具有Internet资源管理器风格的工具栏。按照图8-5设置各选项,单击Next按钮,将弹出MFC AppWizard-Step 5 of 6对话框,如图8-6所示。,图8-6 MFC AppWizard-Step 5 of 6对话框,(6) MFC AppWizard-Step 5 of 6对话框:设置应用程序的风格。在该对话框中可以设置应用程序的风格,具体可以是下面两个值之一:MFC Standard:标准的MFC应用程序;Windows Explorer:具有Windows资源管理器风格的应用程序。在此还可以设置是否在应用程序向导生成的代码中加注注释。它可以是下
14、面两个值之一:Yes,please:在向导生成的代码中加注注释;No,thank you:在向导生成的代码中不加注注释。,同时在该对话框中还可以设置使用MFC库文件的方式,它可以是下面两个值之一:As a shared DLL:以共享动态链接库的方式使用MFC库文件;As a statically linked library:以静态链接库的方式使用MFC库文件。按照图8-6选择默认选项,单击Next按钮,将弹出MFC AppWizard-Step 6 of 6对话框,如图8-7所示。,图8-7 MFC AppWizard-Step 6 of 6对话框,(7) MFC AppWizard-St
15、ep 6 of 6:修改类的信息。在MFC AppWizard-Step 6 of 6对话框中,可以设置向导生成的文件名和类名。在设置好文件名和类名后,单击Finish按钮,将弹出新建工程信息对话框,该对话框列出了关于新建立的应用程序项目文件的相关信息,如图8-8所示。,图8-8 新建项目信息对话框,(8) 单击图8-8中的OK按钮,应用程序向导所有的工作就全部完成了,这时向导已经为用户生成了一个可执行的应用程序框架,如图8-9所示。,图8-9 MFC自动生成的应用程序框架,(9) 编译运行。MFC AppWizard已经生成了一个基本的应用程序,Mymfc应用程序的运行结果如图8-10所示。
16、,图8-10 Mymfc应用程序的运行结果,8.2.2 查看AppWizard生成的信息1. 生成的C+类这里建立的是一个基于文档/视图结构、单文档、无OLE、无数据库选项的最基本的程序框架所需的类(文档/视图结构将在后面详细介绍),所以MFC AppWizard为Mymfc程序生成了四个类。l CMymfcApp:应用程序类,负责程序的初始化、运行以及结束处理;l CMainFrame:主窗口类,负责主窗口的创建、显示和搜索用户命令以及消息派发;l CMymfcDoc:文档类,负责应用程序文档的装载和维护,文档是应用程序需要保存的任何内容;,l CMymfcView:视图类,负责为文档提供一
17、个或几个视图,视图的作用是为显示和修改文档提供人机界面。它们的基类及其声明和定义文件如表8-1所示。,表8-1 MFC AppWizard生成的类,2. 生成的文件AppWizard创建项目时,在指定的Mymfc目录下创建了许多文件,这些文件包含了框架程序的所有的类、全局变量的声明和定义,如图8-11所示。,图8-11 Mymfc目录下创建的文件,1) 工作区、项目和类信息文件AppWizard生成的应用程序框架是通过项目工作区来管理的。所以,无论选择什么类型的应用程序,AppWizard都要为应用程序生成相应的工作区、项目和类信息文件。本例的工作区、项目和类信息文件如表8-2所示。,表8-2
18、 Mymfc应用程序的工作区、项目和类信息文件,2) 应用程序头文件和源文件根据应用程序的类型单文档、多文档或基于对话框,AppWizard将自动创建一些应用程序源文件和头文件,这些文件分别是应用程序类、对话窗口类等的声明文件和类实现文件。在本例中,AppWizard生成的头文件和源文件如表8-3所示。,表8-3 Mymfc应用程序的头文件和源文件,3) 资源文件4) StdAfx.cpp和StdAfx .h 8.2.3 浏览项目信息与添加代码1浏览项目信息前面生成的Mymfc应用程序在项目工作区窗口中显示如图8-12所示。,图8-12 Mymfc项目工作区窗口,切换到ClassView中,展
19、开ClassView顶层的文件夹后,显示Mymfc项目中所包含的所有的类。双击CMymfcApp类的图标(或单击图标旁的+号)时,ClassView展开该类并显示其类成员,如图8-13所示。,图8-13 项目Mymfc的类视图,项目中增减C+类或者在类中增减成员,都可以在ClassView中进行。切换工作区视图到ResourceView,可以看到AppWizard自动为Mymfc程序生成的资源名称和类型,资源类型包括图标、菜单、加速键、工具栏、版本信息等,如图8-14所示。,图8-14 项目Mymfc的资源视图,2添加代码添加成员变量一般有两种方法:第一种方法是利用VC+的可视化编程工具,具体
20、步骤为:在Workspace窗口的ClassView选项卡中选择要添加成员变量的类。右击此类,在弹出菜单中单击Add Member Variable选项,将出现如图8-15所示的Add Member Variable对话框。,图8-15 Add Member Variable对话框,8.3 MFC应用程序框架与 Win32程序的关联,8.3.1 MFC应用程序框架对Win32程序的封装1Windows程序的基本结构现在我们复习一下第6章中介绍的Win32程序的基本结构,如下所示。,WinMain()WNDCLASS wndclass ;RegisterClass ( While (GetMes
21、sage (&msg,NULL,0,0),TranslateMessage (WndProc (hwnd, message, wParam, lParam),switch (message)case case WM_DESTROY:PostQuitMessage (0);default:return DefWindowProc (hwnd,message,wParam,lParam);return (0);,2MFC类对WinMain和WndProc的封装在MFC类中,与应用程序框架相关的类如图8-16所示。,图8-16 MFC应用程序相关的几个类,通常在应用程序中,我们直接操作的不是上面这些
22、类,而是以这些类为基类派生出来的类。例如,在Mymfc程序中,AppWizard生成的类与这些类的派生关系如下:基类 派生类CWinApp CMymfcAppCFrameWnd CMainFramCDocument CMymfcDocCView CMymfcView,首先,我们看看MFC类库的类定义(afxwin .h),把CWinApp和CFrameWnd类与应用程序框架相关的最重要细节摘录出来,class CWinApp : public CWinThreadDECLARE_DYNAMIC(CWinApp)public:/ ConstructorCWinApp(LPCTSTR lpszAp
23、pName = NULL); / app name defaults to EXE name,/ Attributes/ Startup args (do not change)HINSTANCE m_hInstance;HINSTANCE m_hPrevInstance;LPTSTR m_lpCmdLine;int m_nCmdShow;/ Running args (can be changed in InitInstance)LPCTSTR m_pszAppName; / human readable nameLPCTSTR m_pszRegistryKey; / used for re
24、gistry entriesCDocManager* m_pDocManager;,/ Support for Shift+F1 help mode.BOOL m_bHelpMode; / are we in Shift+F1 mode?public: / set in constructor to override defaultLPCTSTR m_pszExeName; / executable name (no spaces)LPCTSTR m_pszHelpFilePath; / default based on module pathLPCTSTR m_pszProfileName;
25、 / default based on app name,/ Initialization Operations - should be done in InitInstancevirtual BOOL InitApplication();/ Command Handlersprotected:/ map to the following for file new/openafx_msg void OnFileNew();afx_msg void OnFileOpen();,/ Implementationpublic: / public for implementation accessvi
26、rtual BOOL InitInstance();virtual int ExitInstance(); / return app exit codevirtual int Run();virtual BOOL OnIdle(LONG lCount); / return TRUE if more idle processingvirtual LRESULT ProcessWndProcException(CException* e, const MSG* pMsg);,public:virtual CWinApp();DECLARE_MESSAGE_MAP();虽然在CWinApp类定义中未
27、见到WinMain函数,但是我们见到了以下几个虚函数,如:InitApplication();InitInstance();Run(),下面再来看看CFrameWnd的定义:class CFrameWnd : public CWndDECLARE_DYNCREATE(CFrameWnd)/ Constructorspublic:static AFX_DATA const CRect rectDefault;CFrameWnd();BOOL LoadAccelTable(LPCTSTR lpszResourceName);,BOOL Create(LPCTSTR lpszClassName,LP
28、CTSTR lpszWindowName,DWORD dwStyle = WS_OVERLAPPEDWINDOW,const RECT,/ Implementation public:virtual CFrameWnd(); protected:virtual BOOL PreCreateWindow(CREATESTRUCT / default to delete this.,/AFX_MSG(CFrameWnd)/ Windows messagesafx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);afx_msg void OnDestr
29、oy();afx_msg void OnClose();friend class CWinApp;,在CFrameWnd中,afx_msg表示后面的成员函数是消息处理函数。其中,有下列三个函数:afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);afx_msg void OnDestroy();afx_msg void OnClose();这三个消息处理函数分别处理以下窗口消息:消息处理函数 消息OnCreate() WM_PAINTOnDestroy() WM_DESTROYOnClose() WM_CLOSE,8.3.2 MFC应用程序的
30、启动流程下面是Mymfc程序的启动流程,如图8-17所示。,图8-17 Mymfc程序流程,需要解释的地方有以下几点:(1) 应用程序类:CWinApp的派生类,该类的每一个对象代表一个应用程序。程序中默认定义一个全局的对象,即theApp。(2) Main函数:启动应用程序时,Windows调用应用程序框架内置的WinMain函数,WinMain寻找由CWinApp派生出的全局构造的应用程序对象theApp。(3) 函数InitInstance:当WinMain函数找到应用程序对象theApp时,它调用虚成员函数InitInstance,这个成员函数调用所需的构造动态(在程序运行时)创建主窗
31、口对象、视图对象和文档对象,以及主框架窗口和视图窗口,并显示应用程序的主框架窗口和视图窗口。,(4) 成员函数Run:函数Run隐藏在基类CWinApp中,它负责从消息队列中读取应用程序的消息到窗口,以保持应用程序的正常运行。在WinMain调用InitInstance之后,便调用Run函数。(5) 成员函数ExitInstance:如果用户通过关闭主框架窗口来关闭应用程序,这个操作就将激发一系列事件的发生,包括从Run中退出,从WinMain中退出和应用程序对象的析构。,实际的Mymfc程序或者MFC应用程序的流程比Win32程序要复杂许多,主要差别在于以下几点:(1) 程序的主函数名为全局
32、函数AfxWinMain()而不是WinMain()。(2) 一般MFC应用程序在InitInstance函数中,不仅创建了窗口对象和主窗口,还会创建视图对象和视图窗口、文档对象。(3) 读取应用程序的消息队列的代码在CWinThread:Run()中。因为CWinThread是CWinApp的基类,所以CWinApp:Run()会调用CWinThread:Run()。,8.4 MFC应用程序的消息映射,8.4.1 消息类别1窗口消息对于窗口消息,描述它的三个主要字段消息类别messsge、附加参数wParam和lParam的格式如下:messsge wParam lParamWM_XXX 随
33、WM_XXX而变 随WM_XXX而变,窗口消息的产生一般包含下列途径:与窗口交互产生;系统产生。2命令消息命令消息一般与处理用户的某个请求或者执行用户的某个指令有关。描述它的三个主要字段消息类别messape、附加参数wParam和lParam的格式如下:messsge wParam lParamWM_COMMAND 底16位为命令ID高16位为00L,命令消息的产生一般包含下列途径:选择菜单项;单击工具栏按钮;按加速键;程序产生命令。3控件消息描述控件消息的三种格式如下:,(1) 仿窗口消息格式。这种格式遵循窗口消息格式,是窗口消息集的一部分。格式为:messsge wParam lPara
34、mWM_XXX 随WM_XXX而变 随WM_XXX而变(2) 仿命令消息格式。此格式遵循命令消息格式,但是与命令消息的附加参数有区别。格式如下:messsge wParam lParamWM_COMMAND 低16位为控件ID,高16位为消息通知码 控件窗口句柄,(3) 单独控件消息格式。这种格式是一种流行的控件消息表示格式,消息类别码为WM_NOTIFY,格式为:messsge wParam lParamWM_NOTIFY 控件ID 指向NMHDR的指针,8.4.2 消息映射原理MFC应用程序对消息的处理大致分为两个阶段。第一阶段,使用应用程序类的Run()函数把消息从应用程序的消息队列提取
35、出来,并且发送到目标类对象,即主框架窗口类对象;第二阶段,因为所有消息的最终目标并不一定是主窗口类对象,也可能是其他类。8.4.3 消息处理过程将经历下列过程:(1)应用程序类Run()函数调用:DispatchMessage(MSG *msg)把消息交给全局函数LRESULT AfxWndPorc(HWND hwnd,UINT nmsg,WPARAM wParam,LPARAM lParam)。,(2) AfxWndPorc()接收消息,并寻找消息的目标窗口CWnd对象,然后调用LRESULT AfxCallWndProc(CWnd *pWnd,HWND hwnd,UINT msg,WPAR
36、AM wParam,LPARAM lParam)。(3) AfxCallWndProc()存储消息的消息标志符和参数,然后调用目标窗口对象的虚函数LRESULT WndProc(UINT nmsg,WPARAM wParam,LPARAM lParam)。(4) WndProc()将发送消息给BOOL OnWndMsg(UINT nmsg,WPARAM wParam,LPARAM lParam,LRESULT *pResult)。OnWndMsg()负责将消息分类为窗口消息、命令消息和控件消息三种,不同的消息类别将交给不同的消息处理函数去处理。,(5) 如果消息类别是WM_COMMAND,则O
37、nWndMsg()将调用LRESUL OnCommand(WPARAM wParam,LPARAM lParam);如果消息类别是WM_NOTIFY,则OnWndMsg()将调用BOOL OnNotify(WPARAM wParam,LPARAM lParam,LRESULT pResult);其他则认为是窗口消息,OnWndMsg()将搜索目标窗口类或者基类的消息映射表,以判断该类及其基类能否处理该消息。如果不能处理此消息,OnWndMsg()将把消息交给LRESULT DefWindowProc(UINT nmsg,WPARAM wParam,LPARAM lParam)函数进行默认处理。
38、,(6) OnCommand()继续判断消息lParam参数是否为0。如果不为0,则为控件消息,将通知控件处理该消息;如果lParam参数为0或者控件不能处理该消息,将调用BOOL OnCmdMsg(UINT nID,int nCode,void *pExtra,AFX_CMDHANDLERlNFO *pHandlerlnfo),OnCmdMsg()将按照一定的路径搜索相应类的消息映射表,以确定能否处理该消息。(7) OnNotify()将通知控件处理该消息:如果控件不能处理该消息,则也将调用OnCmdMsg()按照一定的路径搜索相应类的消息映射表。,(8) OnCmdMsg()按照已经规定好的路径搜索相应类的消息映射表,以便找到消息处理函数并执行,如果不能找到,则把相应的界面元素(如菜单项)变灰。OnCmdMsg()把搜索的结果反馈给OnNotify()和OnCommand(),然后它们分别把处理的结果反馈给OnCmdMsg()函数,以便确定OnCmdMsg()能否处理该消息。,