C及Windows可视化程序设计 刘振安著.pptx
本章将介绍如何使用MFC类库编程,并通过一个简单的例子说明MFC的消息处理机制。然后模拟文档/视结构编制一个程序,通过它引入使用向导和文档/视结构的概念,为学习下一章的文档/视结构打下基础。最后给出一个基于对话框风格的实例。第1页/共142页在使用MFC类库编程之前,先来看一个使用全局对象的例子。【例12.1】使用全局对象启动程序运行。/example.h文件#include using namespace std;class CMyApp int x,y,z;public:CMyApp(int a=0,int b=0,int c=0):x(a),y(b),z(c)coutBegin.endl;12.1 一个使用全局对象的程序第2页/共142页void SetNum(int a,int b,int c)x=a;y=b;z=c;int Add()return x+y+z;/example.cpp文件#include example.hCMyApp theApp;/全局对象void main()coutIn main.endl;theApp.SetNum(2,4,6);couttheApp.Add();coutendlGo out.endl;第3页/共142页程序运行输出如下:Begin.In main.12 Go out.程序是先执行“CMyApp theApp;”创建并初始化惟一全局对象theApp,输出信息“Begin.”,然后进入主程序。Visual C+6.0的程序设计方法也是如此,即先建立并初始化惟一的全局对象theApp。图12.1左边的框中给出这个程序的类图及全局变量图,右边是程序的.cpp文件。第4页/共142页图12.1 Object的类图及全局变量第5页/共142页C+类库运用面向对象技术,大大简化了程序的设计工作。Microsoft公司提供的基础类库MFC(Microsoft Foundation Classes)就是一个常被称为Application Framework的大类库,其中包含用来开发Windows下C+应用程序的一组类,封装了大部分的Windows API,大大加速Windows下C/C+程序员的软件设计。12.2 使用MFC编制Win32 Application程序第6页/共142页习惯上分别用Win16 和Win32区别16位和32位Windows程序,Windows API则是泛指两者。由前面的讨论可知,驾驭数以千计的API函数并非易事。MFC则把这些浩繁的API函数逻辑地组织起来,使它们具有面向对象的抽象性、封装性、继承性和多态性等特点。怎样学习MFC类库,各人意见不一。很多人认为:“学习MFC,最重要的一点是要学会抽象地把握问题,不求甚解”。这可能对减轻学习难度、提高兴趣是有帮助的,不过也有一些问题,即很多人成了代码拼凑机、只见树木不见森林。第7页/共142页其实,在学习开始就了解Windows程序的基本运行原理,并了解MFC是怎样与之结合的,能大大加快理解,让人更易于接受MFC,这样学习似慢实快。相反,很多书从头到尾地教读者如何如何做,最后仍旧不知所以。学习MFC要理解MFC的应用程序框架,熟记其类层次结构,并不需要刻意去记忆众多的类及它们的成员函数。第8页/共142页【例12.2】使用MFC类库编制输出“Hello MFC”的程序。文档/视的结构比较复杂,先不让向导(AppWizard)生成代码,仍然用Visual C+6.0集成开发环境生成一个空的Win32应用程序项目SMFC1。建立SMFC1.cpp文件如下:第9页/共142页#include/使用MFC类库需要包含的头文件class CMyWin:public CFrameWnd public:CMyWin()protected:afx_msg void OnPaint();DECLARE_MESSAGE_MAP()/声明宏;/使用消息宏BEGIN_MESSAGE_MAP(CMyWin,CFrameWnd)ON_WM_PAINT()END_MESSAGE_MAP()第10页/共142页void CMyWinOnPaint()CString str=Hello,MFC!;/设置输出窗口的 /字符串内容CRect rect(240,100,340,200);CPaintDC dc(this);dc.DrawText(str,&rect,DT_CENTER);class CMyApp:public CWinApp public:BOOL InitInstance();第11页/共142页BOOL CMyAppInitInstance()/改写CWinApp的 /InitInstance函数CMyWin*pFrame=new CMyWin;pFrame-Create(0,_T(使用MFC的Win32 Application);/标题条pFrame-ShowWindow(1);/m_nCmdShow=1pFrame-UpdateWindow();this-m_pMainWnd=pFrame;return TRUE;CMyApp theApp;/全局对象第12页/共142页程序在编译前还要设置参数,使它能使用MFC类库。选择“Project”菜单的“Settings”项,弹出如图12.2所示的“Project Settings”对话框,在Microsoft Foundation Classes列表框中选择使用MFC。图12.3是编译运行的示意图,图12.4是类的组成图。这个程序比用Win32 API函数写的要简短一些,而且随着程序规模的增大,会很快显示出它的优点,编程量也会大大减小。第13页/共142页图12.2 设置使用MFC示意图第14页/共142页图12.3 运行示意图第15页/共142页图12.4 类的组成图和全局变量第16页/共142页这个程序声明了两个类:由应用程序类CWinApp派生而来的CMyApp类和由框架窗口类CFrameWnd派生而来的CMyWin类。这两个类在MFC中的地位如图12.5所示。如果写过Win32 API程序,应该能分析出上面程序的来龙去脉。第17页/共142页图12.5 CWinApp类和CFrameWnd类在MFC中的地位示意图第18页/共142页Win32程序中有WinMain和WndProc函数。对于MFC程序来说,这两个函数在哪里?程序如何工作呢?1.全局对象WinMain和WndProc函数被封装在类库里,从而免去用户手写的麻烦。上面用到的CWinApp代表程序体,CFrameWnd则代表一个主框架窗口。MFC的基类名均以字母C开头,习惯上为使用MFC编写的应用程序中的类起名时也是这样做。简单分析第19页/共142页【例12.1】演示了程序使用theApp全局对象。对比图12.1和图12.4的类结构图,它们都有一个全局对象theApp。main在图12.1中以全局函数身份出现,WinMain没有像main那样出现在图12.4中。简单说来,是MFC应用程序将它封装起来,只展现出theApp全局对象。2.WinMain函数寻迹Windows 操作系统在初始化该应用程序进程的同时,将自动为该应用程序创建一个主线程,并将控制权交与C/C+运行时(Runtime)库的启动函数。注意程序中定义了一个全局对象:CMyApp theApp;第20页/共142页此时,其构造函数会被调用,初始化对象的数据成员,随后程序就进入主函数入口了。为了简少程序员的工作量。将原来的大量Windows API分门别类、有组织地进行封装,主函数和窗口函数也不例外。如【例12.2】所示,程序员现在只要从MFC类派生相应的类并产生一个全局对象即可。WinMain所在的位置可参见MFC源码的appmodule.cpp中的如下代码段:extern C int WINAPI_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,第21页/共142页 LPTSTR lpCmdLine,int nCmdShow)/call shared/exported WinMainreturn AfxWinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow);“_t”是为了支持Unicode而准备的一个宏。MFC已为用户准备好这个有相当程度固定行为的函数,它仅仅调用了AfxWinMain。AfxWinMain又做了什么?下面是简化的行为:第22页/共142页int AFXAPI AfxWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)/AfxGetThread()返回指向当前线程的指针CWinThread*pThread=AfxGetThread();/AfxGetApp()获得全局对象theApp /pApp指向全局对象theAppCWinApp*pApp=AfxGetApp();AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow);第23页/共142页/CMyApp继承CwinApp虚函数InitApplication/调用CWinApp InitApplication()pApp-InitApplication();/CMyApp改写CwinApp的 /虚函数 InitApplication /调用CMyApp InitApplication()pThread-InitInstance();pThread-Run();/进入消息循环 AfxWinTerm();return nReturnCode;第24页/共142页AfxGetApp是一个全局函数,返回应用程序对象的指针。这里是取得指向CMyApp类的全局对象theApp的指针。CMyApp继承CWinApp,CMyAppInitInstance()被AfxWinMain间接调用到,于是就创建了应用程序窗口。在第11章解释过如下两个函数:MyRegisterClass(hInstance);/注册窗口 InitInstance(hInstance,nCmdShow)/初始化窗口 如将MyRegisterClass(hInstance)改名为 InitApplication(hInstance),则与InitInstance函数一起,正好成了现在MFC的CWinApp的两个虚函数。第25页/共142页InitApplication负责为每一个程序只注册一次窗口类,而InitInstance则为每一个例程都做一次初始化。所以,CWinApp的派生类CMyApp不需要改写InitApplication虚函数,但需要改写InitInstance虚函数,并在其中把窗口产生出来。调用由CWinApp类继承的Run()成员函数进入消息循环。程序结束时则调用CWinApp的ExitInstance()函数退出。既然WinMain的困惑没有了,WndProc也一样,读者若有兴趣可读一读MFC源码。第26页/共142页3.消息循环在注册了窗口类和创建了窗口之后,就要靠消息循环了。这就该CWinThreadRun()表演了。int CWinThreadRun().if(!PumpMessage()return ExitInstance();.下面是CWinThreadPumpMessage()的代码:第27页/共142页BOOL CWinThreadPumpMessage()if(!GetMessage(&m_msgCur,NULL,NULL,NULL)return FALSE;if(m_msgCur.message!=WM_KICKIDLE&!PreTranslateMessage(&m_msgCur)TranslateMessage(&m_msgCur);DispatchMessage(&m_msgCur);return TRUE;第28页/共142页原来熟悉的消息循环在此。用C写Win32程序时,处理消息是在窗口子过程中的巨大而复杂的switch/case中,而MFC采用消息映射机制来决定如何处理特定的消息。这种消息映射机制包括一组宏、映射成员函数等。剖析相关的宏不是本教材的任务,故这里仅介绍手工编写的方法。虽然以后是用向导而不必手工输入,但初学时还是知道一点原理为好。本程序需要在处理WM_PAINT消息时输出一字符串,就以此为例简单说明消息宏的使用。首先在窗口类(即要处理消息的类,这里是CMyWin)的定义中加入宏:第29页/共142页 DECLARE_MESSAGE_MAP();然后在实现文件中使用一对由 BEGIN_MESSAGE_MAP()开始,END_MESSAGE_MAP()结束的程序体。BEGIN_MESSAGE_MAP()的两个参数分别为处理消息的类及其基类,此两宏建造了一个类似于11.3节所述的大表格;接着制造表中每项,这由ON_.宏完成,它们都是标准的,由MFC定义。例如要处理WM_PAINT消息,就应该用ON_WM_PAINT()宏,用户处理程序默认的是“类名OnPaint()”函数。下面是这个程序的源码:第30页/共142页class CMyWin:public CFrameWnd public:CMyWin()protected:afx_msg void OnPaint();/声明消息处理函数DECLARE_MESSAGE_MAP()/声明宏;/CFrameWnd是CMyWin的基类BEGIN_MESSAGE_MAP(CMyWin,CFrameWnd)ON_WM_PAINT()/对应CMyWinOnPaint()END_MESSAGE_MAP()第31页/共142页从程序中可以看出,具有消息的函数用afx_msg 标识。如图12.5所示,处理消息的基类是CCmdTarget,各个派生类继承这个消息处理机制。回忆11.3节用数组实现的消息处理,很容易发现MFC使用的是同一原理,只是设计了功能强大的宏帮助实现。4.程序结构总结从程序中可以看出,由于MFC已经把Windows API都封装起来了,程序代码已经没有原来熟悉的结构了。简单说来,看不到WinMain,不知道程序从何处开始执行;看不到如何注册窗口类;看不到消息循环和窗口处理函数。第32页/共142页其实,它现在变得很简单。下面假设程序运行处理WM_PAINT消息,可分为准备、运行和结束阶段,分析各个阶段的过程。(1)程序的准备阶段程序的准备工作可分为如下几个过程:theApp是应用程序的全局对象,代表整个程序,其基类是CWinApp,产生theApp,配置内存并建 立初值;函数AfxWinMain通过AfxWinIni函数调用 AfxInitThread函数设置消息队列;AfxWinMain调用Application函数,它是 CWinApp的虚函数,AfxWinMain一般只是调 用而不改写它;第33页/共142页 AfxWinMain调用InitInstance函数,它是 CWinApp的空虚函数,必须重新定义它;定义CWinApp派生类CMyApp的空虚函数 CMyApp InitInstance()。框架对象代表主窗口,其基类是CFrameWnd,CMyWin*pFrame=new CMyWin;语句使用“new”创建一个CMyWin 的对象,程序中 将CMyWin的构造函数设计为空函数;使用函数Create产生主窗口,然后写入标题条内容;执行函数ShowWindow显示窗口;执行函数UpdateWindow,发出WM_PAINT消息;执行函数Run,进入消息循环。第34页/共142页(2)程序的运行阶段程序运行的过程如下:程序由CwinAppRun()中的GetMessage()循环获得WM_PAINT消息;WM_PAINT经由DispatchMessage()送到窗口 函数CWndDefWindowProc()中;CWndDefWindowProc()将消息传递并通过 消息映射表格(Message Map);在传递过程中发现有相符合的项目,就调用项 目中对应的函数,此函数是应用程序利用 BEGIN_MESSAGE_MAP和 END_MESSAGE_MAP之间的宏设立起来的;第35页/共142页系统提供处理标准消息的标准命名方法,例如 WM_PAINT由函数OnPaint处理,这里的程序也 将消息处理函数声明为afx_msg void OnPaint()。(3)程序的结束阶段程序结束的过程为:本程序的CMyWin没有处理WM_CLOSE消息,如果使用“文件”菜单的“关闭”命令,则交给默认 的处理程序,默认函数调用DestroyWindow()并发出WM_DESTROY消息;默认的WM_DESTROY处理方式是调用 PostQuitMessage(),发出WM_QUIT;CWinAppRun()接收WM_OUIT,结束消息 循环;第36页/共142页然后调用CWinApp的虚函数ExitInstrance,但 最终是执行CWinAppExitInstrance(),还是CMyAppExitInstrance(),则决定于CMyApp是否改写了ExitInstrance 函数;最后执行函数AfxWinTerm结束程序。由此可见,我们主要是产生两个派生类。CWinApp类提供的4个关键虚成员函数如下:virtual BOOL InitAppLication();/构建窗口 virtual BOOL InitInstance()/与窗口有关的空函数 virtual in Run();/处理消息 virtual intExitInstance();/结束运行注意:派生类一定要改写虚函数InitInstance。第37页/共142页AppWizard生成的MFC应用程序(非基于对话框的),都建立在文档/视结构之上。所以,在使用AppWizard之前,有必要简介一下此结构。本节通过手工编制多个文件来模拟一个最简单的单文档程序。仍然使用以前的方法,创建一个名为Smfc2的空Win32工程,12.3 模拟文档/视结构的MFC程序第38页/共142页手工建立4个.cpp文件:MainFrame.cpp Smfc2.cpp Doc.cpp View.cpp5个头文件:Doc.h MainFrame.h View.h Smfc2.h resource.h1个资源文件SMFC2.rc等共10个文件。图12.6给出在集成环境中使用FileView观察到的文件结构图,而图12.7则是使用ClassView观察到的类结构图和全局对象theApp。第39页/共142页图12.6 文件结构图第40页/共142页图12.7 类结构图和全局对象theApp第41页/共142页可以直接使用文本编辑器编辑这些文件,然后按图12.6的结构装入项目中,也可以直接在项目中按此图建立文件并输入相应内容。下面不是集中给出.cpp文件,而是先给出头文件,接着给出相应的.cpp文件,这样做的目的是容易对比。第42页/共142页/Smfc2.hclass CMyApp:public CWinApppublic:BOOL InitInstance();/声明改写虚函数InitInstanceDECLARE_MESSAGE_MAP()/声明消息映射(没有 /声明消息处理函数);程序清单第43页/共142页/Smfc2.cpp:#include Doc.h#include MainFrame.h#include View.h#include Smfc2.h#include resource.hBEGIN_MESSAGE_MAP(CMyApp,CWinApp)END_MESSAGE_MAP()/因为没有声明消息处理 /函数,所以没有实现细节BOOL CMyAppInitInstance()/改写虚函数 CSingleDocTemplate*pDocTemplate;/建立单文档模板第44页/共142页 pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CMyDoc),RUNTIME_CLASS(CMainFrame),RUNTIME_CLASS(CMyView);AddDocTemplate(pDocTemplate);CCommandLineInfo cmdInfo;/处理命令行 ParseCommandLine(cmdInfo);if(!ProcessShellCommand(cmdInfo)return FALSE;m_pMainWnd-ShowWindow(SW_SHOW);m_pMainWnd-UpdateWindow();return TRUE;第45页/共142页CMyApp theApp;/声明全局对象/MainFrame.h:#include class CMainFrame:public CFrameWnd protected:DECLARE_DYNCREATE(CMainFrame)afx_msg void OnAppExit();/声明消息处理函数DECLARE_MESSAGE_MAP()/声明消息映射;第46页/共142页/MainFrame.cpp:#include MainFrame.h#include resource.hIMPLEMENT_DYNCREATE(CMainFrame,CFrameWnd)/开始实现宏BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)ON_COMMAND(ID_EXIT,OnAppExit)/实现消息映射表END_MESSAGE_MAP()/结束宏void CMainFrameOnAppExit()this-OnClose();/Doc.h:#include 第47页/共142页class CMyDoc:public CDocumentchar str11;protected:DECLARE_DYNCREATE(CMyDoc)public:char*GetStr();CMyDoc()strcpy(str,Hello,MFC!);virtual void Serialize(CArchive&ar);protected:/没有消息处理函数DECLARE_MESSAGE_MAP();/Doc.cpp:#includeDoc.h第48页/共142页IMPLEMENT_DYNCREATE(CMyDoc,CDocument)BEGIN_MESSAGE_MAP(CMyDoc,CDocument)/没有消 /息处理函数END_MESSAGE_MAP()void CMyDocSerialize(CArchive&ar)if(ar.IsStoring()elsechar*CMyDocGetStr()return str;第49页/共142页/View.h:#include class CMyDoc;class CMyView:public CView protected:DECLARE_DYNCREATE(CMyView)public:CMyDoc*GetDocument();virtual void OnDraw(CDC*pDC);protected:DECLARE_MESSAGE_MAP();第50页/共142页inline CMyDoc*CMyViewGetDocument()return(CMyDoc*)m_pDocument;/View.cpp:#include View.h#include Doc.hIMPLEMENT_DYNCREATE(CMyView,CView)BEGIN_MESSAGE_MAP(CMyView,CView)END_MESSAGE_MAP()void CMyViewOnDraw(CDC*pDC)CMyDoc*pDoc=GetDocument();ASSERT_VALID(pDoc);pDC-TextOut(250,150,pDoc-GetStr();第51页/共142页/resource.h:#define IDR_MAINFRAME1#define ID_EXIT40001/资源文件SMFC2.rc:#include resource.hIDR_MAINFRAME MENU DISCARDABLE BEGIN POPUP 文件 BEGINMENUITEM 退出,ID_EXIT ENDEND第52页/共142页STRINGTABLE DISCARDABLE BEGINIDR_MAINFRAME mfc_singlennMfc_sinn nMfcsingle.DocumentnMfc_si DocumentEND将使用MFC的方式设置为 Use MFC in a Shared DLL编译运行如图12.8所示。第53页/共142页图12.8 模拟文档/视/框架结构程序运行示意图第54页/共142页Document/View(文档/视)互为表里。View本身就是一个窗口,但其外围必须再加上一个框架窗口(这里为CMainFrame)作容器,它是数据的“面”;Document则是数据的“体”。使用文档/视就是要把数据与它的表现分开,让Document专心管理数据,让View专门显示数据。CView要提供一个GetDocument成员函数来获取与视相关的文档的指针,通过该指针可以存取文件文档中的数据。下面对模拟程序进行简要说明。第55页/共142页1.声明消息处理函数和消息映射宏假设一个广义的基类为Base,派生类为Derived(所谓广义,就是以后可以将它们与实际的对应)。例如CwinApp作为基类,自己设计的CMyApp为派生类;CView作为基类,CMyView为派生类;CDocument作为基类,CMyDoc为派生类等。为了介绍简单方便,还假设消息处理函数没有参数,其他与演示消息映射无关的部分均省略。下面是假设有两个处理消息的成员函数的声明:多文件中的消息映射第56页/共142页class Derived:public:Baseprotected:afx_msg void OnFirstName();/声明消息处理函数 DECLARE_MESSAGE_MAP()/声明消息映射;假设这里声明的成员函数是用来处理Windows的COMMAND消息的成员函数,则它们必须遵循两个游戏规则:以afx_msg void为消息处理函数的类型,函数的名字必须以On开始,即形如 afx_msg void OnFirstName的形式。FirstName是假设的字符串,一般与映射表定义有内在联系,详见下节。第57页/共142页接着使用一个宏声明 DECLARE_MESSAGE_MAP()它声明的是一个数据结构,这个数据结构有着规定的格式。其实,它们就是第11.3节演示的结构数组。2.实现消息映射声明消息映射数据结构之后,还需要为这个数据结构填入消息映射的数据。像【例12.2】那样简单的程序,声明和实现是在同一个.cpp文件里。但对现在的多文件来说,是将声明部分放在类Derived的头文件中,实现放在它的.cpp文件中。必须按照DECLARE_MESSAGE_MAP()声明的数据结构格式填写,这由3部分来完成。第58页/共142页第1部分是开始宏。它的格式为:BEGIN_MESSAGE_MAP(Derived,Base)这个宏有两个参数,一个是消息处理函数所属的派生类的类名Derived,一个是Drived的直接基类的类名Base。第2部分由一系列对应的消息宏填写。这相当于填写结构数组的数据,而且填写方式简单。假设处理的是Windows的命令消息,则使用 ON_COMMAND宏这个宏有两个参数,一般的命令消息(WM_COMMAND)的对应规律如下:ON_COMMAND(id,OnFirstName)第59页/共142页 OnFirstName 声明的消息处理函数名 FirstName 如果它用First和Name两个字符 串组成,则大写每一个字符串的 第1个字母,这样做一般是为了 更好地表达函数的功能,例如 FileNew和FileSave等。它们与id的选择有功能意义上的关联。对菜单命令来说,id就是菜单标识符,一般常见如下对应规律:ON_COMMAND(IDM_FirstName,OnFirstName)ON_COMMAND(IDM_Name,OnName)ON_COMMAND(IDM_Name,OnFirstName)下面是系统默认的几个例子:ON_COMMAND(IDM_FILENEW,OnFileNew)第60页/共142页ON_COMMAND(IDM_FILEOPEN,OnFileOpen)ON_COMMAND(IDM_FILESAVE,OnFileSave)ON_COMMAND(IDM_ABOUT,OnAbout)ON_COMMAND(ID_ EXIT,OnAppExit)第3部分是结束宏,格式为:END_MESSAGE_MAP()如果这个文件里没有要处理的消息,则有两种处理的方法:头文件里声明宏,即:class Derived:public:Base protected:DECLARE_MESSAGE_MAP();第61页/共142页在实现文件里给出开始宏和结束宏,即:BEGIN_MESSAGE_MAP(Derived,Base)END_MESSAGE_MAP()注意宏的实现不能放在实现文件中的函数中,它们是单独的结构,不属于任何函数。将它们同时省略。注意不要只省略头文件中的声明或.cpp文件中的实现。为了加深理解,本程序分别对以上两种方法进行了演示。3.实现消息处理在实现文件中编制消息处理函数。例如对于OnFirstName 函数,则有:第62页/共142页void OnFirstName()/函数体定义结论:在头文件里声明派生类,在类中声明消息处理函数之后,声明消息处理宏。在派生类的实现文件中单独实现宏。4.使用实例本小节的例子中只有一个文件需要消息处理,对于菜单项Exit来说,首先在MainFrame.h中声明如下:/MainFrame.h第63页/共142页class CMainFrame:public CFrameWnd/派生关系 protected:DECLARE_DYNCREATE(CMainFrame)afx_msg void OnAppExit();/消息处理函数DECLARE_MESSAGE_MAP()/声明宏;下面是在MainFrame.cpp中的实现:/MainFrame.cppBEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)ON_COMMAND(ID_EXIT,OnAppExit)第64页/共142页END_MESSAGE_MAP()注意:这里菜单标识符ID_EXIT应与它在资源文件中定义的一样。OnAppExit消息处理函数在程序中的实现如下:void CMainFrameOnAppExit()this-OnClose();第65页/共142页资源文件是一种以后缀为.rc的、用文字来描述资源的文件。常用的资源很多,而且随着发展还会有新的资源不断加入。常用的资源有:ACCELERATOR ICON CURSO BITBMP FONT DIALOG VERSIONINFO TOOLBAR MENU使用这些关键字定义资源时,是把被定义的名字放在前面。下面给出定义菜单的一般格式:菜单名MENU描述(可选项)BEGIN/开始定义 POPUP弹出菜单名/菜单条上的名字资源文件第66页/共142页 BEGIN /菜单命令MENUITEM菜单命令名,菜单标识符/继续定义其他菜单命令 END POPUP/继续定义BEGIN/继续定义END/继续定义END/结束定义第67页/共142页菜单体由BEGIN开始,由END结束(也可使用一对“”号)。菜单条的每一个弹出菜单由POPUP指定,在一对BEGIN和END中,使用MENUITEM定义它的菜单命令。一般可以交替使用BEGINEND和“”来区分层次。下面是SMFC2.rc文件:IDR_MAINFRAME MENU DISCARDABLE BEGINPOPUP文件 BEGIN MENUITEM退出,ID_EXITENDEND第68页/共142页STRINGTABLE DISCARDABLE BEGIN IDR_MAINFRAME mfc_singlennMfc_sin nnMfcsingle.DocumentnMfc_siDocumentENDIDR_MAINFRAME是一个资源标识符ID,表示这一文件类型所使用的资源。在.rc文件中代表多种资源,不同类型的资源可以使用相同的ID。上面两种资源都使用这个ID,因为MENU定义了IDR_MAINFRAME,所以SRINGTABLE直接使用。ID_EXIT 是菜单命令的标识符,标识符应该是一个具体的惟一整数值,它们在resource.h文件中定义。第69页/共142页/resource.h#define IDR_MAINFRAME 1#define ID_EXIT 40001这些标识符已经具有惟一整数值,就可以在其他地方引用它们。可以使用Visual C+6.0 可视化生成资源,非常方便。第70页/共142页模拟程序使用了文档模板(Document Template)。它有单文档模板和多文档模板之分,模拟程序使用单文档模板。程序中模拟Document/View概念,CMyView本身虽然已经是一个窗口,但其外围还必须封装一个作为舞台的外框窗口,这由CMainFrame提供。还要设计一个存放的容器,以便MyView能随时提取,这由CMyDoc实现。这三者是一体的,也就是说,程序打开单文档,应产生Document/View/Frame对象。这3个对象由Document Template 管理。这里是单文档,所以由CSingleDocTemplate管理。单文档模板第71页/共142页应用程序必须重定义自己的虚函数InitInstance。下面是它的定义:BOOL CMyAppInitInstance()CSingleDocTemplate*pDocTemplate;pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CMyDoc),RUNTIME_CLASS(CMainFrame),RUNTIME_CLASS(CMyView);AddDocTemplate(pDocTemplate);第72页/共142页程序利用CsingleDocTemplate的构造函数动态建立一个CSingleDocTemplate对象。它的构造函数有4个参数:CSingleDocTemplate:CSingleDocTemplate(UINT nIDResource,CRuntimeClass*pDocClass,CRuntimeClass*pFrameClass,CRuntimeClass*pViewClass);nIDResource是一个资源标识符ID,表示这一文 件类型所使用的资源。在.rc文件中代表多种资源,不同类型的资源可以使用同一个ID。第73页/共142页 本程序在 resource.h中定义的资源标识符ID是 IDR_MAINFRAME 它的定义见SMFC2.rc。因此,这个构造函数的 第1个参数是IDR_MAINFRAME。pDocClass是指针,指向由CDocument派生的 CMyDoc类的CRuntimeClass对象。pFrameClass是指针,指向由CFrameWnd派生 的CMainFrame类的CRuntimeClass对象。pVie