之前void 0CDSAdpcmPlayerView::OnFile()这个函数是实现格式转换的,紧跟着下面的这个函数是做什么的?

VC学习(孙鑫版)_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
喜欢此文档的还喜欢
VC学习(孙鑫版)
V​C​+​+​的​一​个​很​好​的​学​习​笔​记​,​来​源​于​网​络​!
阅读已结束,如果下载本文需要使用
想免费下载本文?
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢Windows+MFC消息2--MFC对象的创建(转)
【娱乐休闲】
【生活服务】
【电脑网络】
【文体教育】
【行业部门】
文章浏览→→→Windows+MFC消息2--MFC对象的创建(转)Windows+MFC消息2--MFC对象的创建(转)
前面几章介绍了MFC的核心概念和思想,即介绍了MFC对Windows对象的封装方法和特点;MFC对象的动态创建、序列化;MFC消息映射机制。现在,考查MFC的应用程序结构体系,即以文档-视为核心的编程模式。学习本章,应该弄清楚以下问题:MFC中诸多MFC对象的关系:应用程序对象,文档对象,边框窗口对象,文档边框窗口对象,视对象,文档模板对象等。MFC对象的创建和销毁:由什么对象创建或销毁什么对象,何时创建,何时销毁?MFC提供了那些接口来支持其编程模式?&MFC对象的关系&&创建关系&这里讨论应用程序、文档模板、边框窗口、视、文档等的创建关系。图5-1大略地表示了创建顺序,但表5-1更直接地显示了创建与被创建的关系。 表5-1 MFC对象的创建关系创建者被创建的对象应用程序对象文档模板文档模板文档文档模板边框窗口边框窗口视 &交互作用关系&应用程序对象有一个文档模板列表,存放一个或多个文档模板对象;文档模板对象有一个打开文档列表,存放一个或多个已经打开的文档对象;文档对象有一个视列表,存放显示该文档数据的一个或多个视对象;还有一个指针指向创建该文档的文档模板对象;视有一个指向其关联文档的指针,视是一个子窗口,其父窗口是边框窗口(或者文档边框窗口);文档边框窗口有一个指向其当前活动视的指针;文档边框窗口是边框窗口的子窗口。Windows管理所有已经打开的窗口,把消息或事件发送给目标窗口。通常,命令消息发送给主边框窗口。图5-2大略地表示了上述关系:&MFC提供了一些函数来维护这些关系。表5-2列出了从一个对象得到相关对象的方法。表5-2 从一个对象得到另一个对象的方法本对象要得到的对象使用的成员函数CDocument对象视列表GetFirstViewPositionGetNextView文档模板GetDocTemplateCView对象文档对象GetDocument边框窗口GetParentFrameCMDIChildWnd或CFrameWnd对象活动视GetActiveView活动视的文档GetActiveDocumentCMDIFrameWnd对象活动文档边框窗口MDIGetActive 表5-3 从一个对象通知另一个对象的方法:本对象要通知的对象/动作使用的成员函数CView对象通知文档更新所有视CDocument::UpdateAllViewsCDocument对象更新一个视CView::OnUpdateCFrameWnd或CMDIFrameWnd对象通知一个视为活动视CView::OnActivateView设置一个视为活动视SetActivateView&可以通过表5-2得到相关对象,再调用表5-3中相应的函数。例如:视在接受了新数据或者数据被修改之后,使用表5-2中的函数GetDocument得到关联文档对象,然后调用表5-3中的文档函数UpdateAllViews更新其他和文档对象关联的视。在表5-2和表5-3中,CView对象指CView或派生类的实例;成员函数列中如果没有指定类属,就是第一列对象的类的成员函数。&MFC提供的接口&MFC编程就是把一些应用程序特有的东西填入MFC框架。MFC提供了两种填入的方法:一种就是使用前一章论述的消息映射,消息映射给应用程序的各种对象处理各种消息的机会;另一种就是使用虚拟函数,MFC在实现许多功能或者处理消息、事件的过程中,调用了虚拟函数来完成一些任务,这样就给了派生类覆盖这些虚拟函数实现特定处理的机会。下面两节将列出两类接口,有两个目的:一是为了让读者获得整体印象,二是后文将涉及到或者讨论其中的许多函数时,不显得突兀。&虚拟函数接口&几乎每一个MFC类都定义和使用了虚拟成员函数,程序员可以在派生类中覆盖它们。一般,MFC提供了这些函数的缺省实现,所以覆盖函数应该调用基类的实现。这里给出一个MFC常用虚拟函数的总览表(见表5-4),更详细的信息或它们的缺省实现动作参见MFC文档。由于基类的虚拟函数被派生类继承,所以在派生类中不作重复说明。覆盖基类的虚拟函数可以通过ClassWizard进行,不过,并非所有的函数都可以这样,有的必须手工加入函数声明和实现。表5-4 常见MFC类的虚拟函数接口类虚拟函数覆盖的目的和功能CCmdTargetOnCmdMsg发送、派发命令消息OnFinalReleaseOLE用途,引用为0时作清理工作CWinThreadExitInstance在线程退出时作清理工作InitInstance在线程开始时作初始化OnIdle执行thread-specific idle-time处理PreTranslateMessage在消息送给Windows函数TranslateMessage andDispatchMessage.之前进行消息过滤IsIdleMessage检查是否是某个特别的消息ProcessWndProcException截获线程消息/命令处理中的例外ProcessMessageFilter线程消息过滤Run实现线程特定的消息循环CWinAppHideApplication关闭所有的窗口之前隐藏应用程序CloseAllDocument退出程序之前关闭所有文档转下页& 续表 SaveModifiedDocument框架窗口关闭时用来保存文档DoMessageBox实现客户化的messageboxDoWaitCursor关闭或打开等待光标OnDDeCommand响应DDE命令WinHelp调用WinHelp函数CWndWindowProc提供一个窗口过程DefWindowProc为应用程序不处理的消息提供缺省处理PostNcDestroy在窗口销毁之后被消息处理函数OnNcDestroy调用OnNotify处理通知消息WM_NOTIFYOnChildNotify父窗口调用它给控制子窗口一个机会来处理通知反射消息DoDataExchangeUpdata调用它来进行对话框数据交换和验证CFrameWndGetMessageBar返回一个指向框架窗口的状态条的指针OnCreateClient创建框架的客户窗口OnSetPreviewMode设置程序的主框架窗口进入或退出打印预览模式NegotiateBorderSpace协调边框窗口的边框空间的大小(OLE用途)CMDIFrameWndCreateClient创建CMDIFrameWnd的MDICLIENT窗,被CWnd的消息处理函数OnCreate调用.转下页& 续表 GetWindowMenuPopup返回窗口的弹出式菜单CDialogOnInitDialog对话框窗口的初始化OnSetFont设置对话框控制的文本字体OnOK模式对话框的OK按钮按下后进行的处理OnCancel模式对话框的CANCEL按钮按下后进行的处理CViewIsSelected测试是否有一个文档被选择(OLE支持)OnActivateView视窗口激活时调用OnActivateFrame当包含视窗口的框架窗口变成活动或非活动窗口时调用OnBeginPrinting打印工作开始时调用,用来分配GDI资源OnDraw用来屏幕显示、打印、打印预览文档内容OnEndPrinting打印工作结束时调用,释放GDI资源OnEndPrintPreview退出打印预览模式时调用OnPrepareDCOnDraw或OnPrint之前调用,用来准备设备描述表OnPreparePrinting文档打印或者打印预览前调用,可用来初始化打印对话框OnPrint用来打印或打印预览文档OnUpdate用来通知一个视的关联文档内容已经变化CDocTemplateMatchDocType确定文档类型和文档模板匹配时的可信程度转下页& 续表 CreateNewDocument创建一个新的文档CreateNewFrame创建一个包含文档和视的框架窗口InitialUpdateFrame初始化框架窗口,必要时使它可见SaveAllModified保存所有和模板相关的而且修改了的文档CloseAllDocuments关闭所有和模板相关的文档OpenDocumentFile打开指定路径的文件SetDefaultTitle设置文档窗口缺省显示的标题CDocumentCanCloseFrame在关闭显示该文档的边框窗口之前调用DeleteContents用来清除文档的内容OnChangedViewList在与文档关联的视图被移走或新加入时调用OnCloseDocument用来关闭文档OnNewDocument用来创建新文档OnOpenDocument用来打开文档OnSaveDocument以来保存文档ReportSaveLoadException处理打开、保存文档操作失败时的例外GetFile返回一个指向Cfile对象的指针ReleaseFile释放一个文件以便其他应用程序可以使用SaveModified用来询问用户文档是否需要保存PreCloseFrame在框架窗口关闭之前调用 &消息映射方法和标准命令消息&窗口对象可以响应以“WM_”为前缀的标准Windows消息,消息处理函数名称以“ON”为前缀。不同类型的Windows窗口处理的Windows消息是有所不同的,因此,不同类型的MFC窗口实现的消息处理函数也有所不同。例如,多文档边框窗口能处理WM_MDIACTIVATE消息,其他类型窗口就不能。程序员从一定的MFC窗口派生自己的窗口类,对感兴趣的消息,覆盖基类的消息处理函数,实现自己的消息处理函数。所有的命令目标(CCmdTarger或导出类对象)可以响应命令消息,程序员可以指定应用程序对象、框架窗口对象、视对象或文档对象等来处理某条命令消息。一般地,尽量由与命令消息关系密切的对象来处理,例如隐藏/显示工具栏由框架窗口处理,打开文件由应用程序对象处理,数据变化的操作由文档对象处理。对话框的控制子窗口可以响应各类通知消息。&对于命令消息,MFC实现了一系列标准命令消息处理函数。标准命令ID在afxres.h中定义。表5-5列出了MFC标准命令的实现,从ID或者函数名可以大致地看出该函数的目的、功用,具体的实现有的后续章节会讲解,详细参见MFC技术文档。程序员可以自己来处理这些标准消息,也可以通过不同的类或从不同的类导出自己的类来处理这些消息,不过最好遵循MFC的缺省实现。比如处理ID_FILE_NEW命令,最好由CWinApp的派生类处理。 表5-5 标准命令消息处理函数ID函数实现函数的类ID_FILE_NEWOnFileNewCWinAppID_FILE_OPENOnFileOpenCWinAppID_FILE_CLOSEOnFileCloseCDocumentID_FILE_SAVEOnFileSaveCDocumentID_FILE_SAVE_ASOnFileSaveAsCDocumentID_FILE_SAVE_COPY_ASOnFileSaveCopyAsCOleServerDocID_FILE_UPDATEOnUpdateDocumentCOleServerDocID_FILE_PAGE_SETUPOnFilePrintSetupCWinApp转下页& 续表ID_FILE_PRINTOnFilePrintCViewID_FILE_PRINT_PREVIEWOnFilePrintPreviewCViewID_FILE_MRU_FILE1...FILE16OnUpdateRecentFileMenuCWinAppID_EDIT_CLEAR&CView没有实现,ID_EDIT_CLEAR_ALL&但是,如果有实现ID_EDIT_COPY&函数,就是派生类ID_EDIT_CUT&CEditView的ID_EDIT_FIND&实现函数ID_EDIT_PASTE_LINK&&ID_EDIT_PASTE_SPECIAL&&ID_EDIT_REPEAT&&ID_EDIT_REPLACE&&ID_EDIT_SELET_ALL&&ID_EDIT_UNDO&&ID_WINDOW_NEWOnWindowNewCMDIFrameWndID_WINDOW_ARRANGEOnMDIWindowCmdCMDIFrameWndID_WINDOW_CASCADE&&ID_WINDOW_TILE_HORZ&&ID_WINDOW_TILE_VERT&&ID_WINDOW_SPLIT&CSplitterWndID_APP_ABOUT&&ID_APP_EXITOnAppExitCWinAppID_HELP_INDEXOnHelpIndexCWinAppID_HELP_USINGOnHelpUsingCWinAppID_CONTEXT_HELPOnContextHelpCWinApp转下页& 续表ID_HELPOnHelpCWinAppID_DEFAULT_HELPOnHelpIndexCWinAppID_NEXT_PANEOnNextPaneCmdCSplitterWndID_PREV_PANEOnNextPaneCmdCSplitterWndID_OLE_INSERT_NEW&&ID_OLE_EDIT_LINKS&&ID_OLE_VERB_FIRST...LAST&&ID_VIEW_TOOLBAR&CFrameWndID_VIEW_STATUS_BAR&CFrameWndID_INDICATOR_CAPSID_INDICATOR_NUMID_INDICATOR_SCRLID_INDICATOR_KANAOnUpdateKeyIndicatorCFrameWnd &MFC对象的创建过程&应用程序使用MFC的接口是把一些自己的特殊处理填入MFC框架,这些处理或者在应用程序启动和初始化的时候被调用,或者在程序启动之后和用户交互的过程中被调用,或者在程序退出和作清理工作的时候被调用。这三个阶段中,和用户交互阶段是各个程序自己的事情,自然都不一样,但是程序的启动和退出两个阶段是MFC框架所实现的,是MFC框架的一部分,各个程序都遵循同样的步骤和规则。显然,清楚MFC框架对这两个阶段的处理是很有必要的,它可以帮助深入理解MFC框架,更好地使用MFC框架,更有效地实现应用程序特定的处理。MFC程序启动和初始化过程就是创建MFC对象和Windows对象、建立各种对象之间的关系、把窗口显示在屏幕上的过程,退出过程就是关闭窗口、销毁所创建的Windows对象和MFC对象的过程。所以,下面要讨论几种常用MFC对象的结构,它们是构成一个文档-视模式应用程序的重要部件。&应用程序中典型对象的结构&本节将主要分析应用程序对象、文档对象、文档模板等的数据结构。通过考察类的结构,特别是成员变量结构,弄清它的功能、目的以及和其他类的关系;另外,在后续有关分析中必定会提到这些成员变量,这里先作个说明,到时也不会显得突兀。下面几节以表格的形式来描述各个类的成员变量。表格中,第一列打钩的表示是MFC类库文档有说明的;没打钩的在文档中没有说明,如果是public,则可以直接访问,但随着MFC版本的变化,以后MFC可能不支持这些成员;第二列是访问属性;第三列是成员变量名称;第四列是成员变量的数据类型;第五列是对成员变量的功能、用途的简要描述。&应用程序类的成员变量&应用程序对象的数据成员表由两部分组成,第一部分是CWinThread的成员变量,如表5-6所示,CWinApp继承了CWinThread的数据成员。第二部分是CWinApp自己定义的成员变量,如表5-7所示。表5-6 CwinThread的成员变量 访问限制变量名称类型解释√publicm_bAutoDeleteBOOL指定线程结束时是否销毁线程对象本身√publicm_hThreadHANDLE当前线程的句柄√publicm_nThreadIDUINT当前线程的ID√publicm_pMainWndCWnd*指向应用程序主窗口的指针√publicm_pActiveWndCWnd*当OLE SERVER就地激活时指向客户程序主窗口的指针 publicm_msgCurMSG当前消息(MSG结构) publicm_pThreadParamsLPVOID传递给线程开始函数的参数 publicm_pfnThreadProc函数指针1线程开始函数,AFX_THREADPROC类型 publicm_lpfnOleTermOrFreeLib函数指针2OLE用途,void (AFXAPI * fn)(BOOL,BOOL) publicm_pMessageFilter指针OLE消息过滤,指向COleMessageFilter对象 protectedm_ptCursorLastCPoint最新鼠标位置 protectedm_nMsgLastUINT消息队列中最新接收到的消息 表5-7 CWinApp的成员变量& 访问限制变量名称类型解释√publicm_pszAppNameLPCTSTR应用程序名称√publicm_hInstanceHINSTANCE标志应用程序当前实例句柄√publicm_hPrevInstanceHINSTANCE32位程序设为空√publicm_lpCmdLineLPTSTR指向应用程序的命令行字符串√publicm_nCmdShowint指定窗口开始的显示方式√publicm_bHelpModeBOOL标识用户是否在上下文帮助模式√publicm_pszExeNameLPCTSTR应用程序的模块名√publicm_pszHelpFilePathLPCTSTR应用程序的帮助文件名,缺省时同模块名√publicm_pszProfileNameLPCTSTR应用程序的INI文件名,缺省时同应用程序名√publicm_pszRegistryKeyLPCTSTRRegister入口,如果不指定,使用INI文件。 publicm_pDocMCDocManager *指向一个文档模板管理器 protectedm_hDevModeHGLOBAL打印设备模式 protectedm_hDevNamesHGLOBAL打印设备名称 protectedm_dwPromptContextDWORD被MESSAGE BOX覆盖的帮助上下文 protectedm_nWaitCursorCountint等待光标计数 protectedm_hcurWaitCursorRestoreHCURSOR保存的光标,在等待光标之后恢复 protectedm_pRecentFileList指针指向CRecentFileList对象,最近打开的文件列表 publicm_atomAppATOMDDE用途 publicm_atomSystemTopicm_atomAppDDE用途 publicm_nNumPreviewPagesUINT缺省被打印的页面 publicm_nSafetyPoolSizesize_t理想尺寸 publicm_lpfnDaoTerm函数指针DAO初始化设置时使用&CDocument的成员变量&表5-8 文档对象的属性。 访问限制变量名称类型解释 protectedm_strTitleCString文档标题 protectedm_strPathNameCString文档路径 protectedm_pDocTemplateCDocTemplate*指向文档模板的指针 protectedm_viewListCPtrList关联的视窗口列表 protectedm_bModifiedBOOL文档是否有变化、需要存盘 publicm_bAutoDeleteBOOL关联视都关闭时是否删除文档对象 publicm_bEmbeddedBOOL文档是否由OLE创建&文档模板的属性&表5-9列出了文档模板的成员变量,5-10列出了单文档模板的成员变量,5-11列出了多文档模板的成员变量。单、多文档模板继承了文档模板的成员变量。表5-9 文档模板的数据成员 访问限制变量名称类型解释 publicm_bAutoDeleteBOOL  publicm_pAttachedFactoryCObject *  publicm_hMenuInPlaceHMENU就地激活时,OLE客户程序的菜单 publicm_hAccelInPlaceHACCEL就地激活时,OLE客户程序的快捷键 publicm_hMenuEmbeddingHMENU  publicm_hAccelEmbeddingHACCEL  publicm_hMenuInPlaceServerHMENU  publicm_hAccelInPlaceServerHACCEL  protectedm_nIDResourceUINT框架、菜单、快捷键等的资源ID protectedm_nIDServerResourceUINT  publicm_nIDEmbeddingResourceUINT  publicm_nIDContainerResourceUINT  publicm_pDocClassCRuntimeClass*指向文档类的动态创建信息 publicm_pFrameClassCRuntimeClass*指向框架类的动态创建信息 publicm_pViewClassCRuntimeClass*指向视类的动态创建信息,由字符串m_nIDResource描述 publicm_pOleFrameClassCRuntimeClass*指向OLD框架类的动态创建信息 publicm_pOleViewClassCRuntimeClass*  publicm_strDocStringsCString描述该文档类型的字符串表5-10 单文档模板的成员变量 访问限制变量名称类型解释 protectedm_pOnlyDocCDocment*指向唯一的文档对象表5-11 单文档模板的成员变量 访问限制变量名称类型解释 publicm_hMenuSharedHMENU该模板的MDI子窗口的菜单 publicm_hAccelTableHACCEL该模板的MDI子窗口的快捷键 protectedm_docListCPtrList该模板的文档列表 protectedm_nUntitledCountUINT用来生成文件名的数字,如”untitled0”的0。&WinMain入口函数&&WinMain流程&现在讨论MFC应用程序如何启动。WinMain函数是MFC提供的应用程序入口。进入WinMain前,全局应用程序对象已经生成。WinMain流程如图5-3所示。图中,灰色框是对被调用的虚拟函数的注释,程序员可以或必须覆盖它以实现MFC要求的或用户希望的功能;大括号所包含的图示是相应函数流程的细化,有应用程序对象App的初始化、Run函数的实现、PumpMessage的流程,等等。从图中可以看出:(1)一些虚拟函数被调用的时机对应用程序类(线程类)的InitIntance、ExitInstance、Run、ProcessMessageFilter、OnIdle、PreTranslateMessage来说,InitInstance在应用程序初始化时调用,ExitInstance在程序退出时调用,Run在程序初始化之后调用导致程序进入消息循环,ProcessMessageFilter、OnIdle、PreTranslateMessage在消息循环时被调用,分别用来过滤消息、进行Idle处理、让窗口预处理消息。(2)应用程序对象的角色首先,应用程序对象的成员函数InitInstance被WinMain调用。对程序员来说,它就是程序的入口点(真正的入口点是WinMain,但MFC向程序员隐藏了WinMain的存在)。由于MFC没有提供InitInstance的缺省实现,用户必须自己实现它。稍后将讨论该函数的实现。其次,通过应用程序对象的Run函数,程序进入消息循环。实际上,消息循环的实现是通过CWinThread::Run来实现的,图中所示的是CWinThread::Run的实现,因为CWinApp没有覆盖Run的实现,程序员的应用程序类一般也不用覆盖该函数。(3)Run所实现的消息循环它调用PumpMessage来实现消息循环,如果没消息,则进行空闲(Idle)处理。如果是WM_QUIT消息,则调用ExitInstance后退出消息循环。(4)CWinThread::PumpMessage该函数在MFC函数文档里没有描述,但是MFC建议用户使用。它实现获取消息,转换(Translate)消息,发送消息的消息循环。在转换消息之前,调用虚拟函数PreTranslateMessage对消息进行预处理,该函数得到消息目的窗口对象之后,使用CWnd的WalkPreTranslateTree让目的窗口及其所有父窗口得到一个预处理当前消息的机会。关于消息预处理,见消息映射的有关章节。如果是WM_QUIT消息,PumpMessage返回FALSE;否则返回TRUE。&MFC空闲处理&MFC实现了一个Idle处理机制,就是在没有消息可以处理时,进行Idle处理。Idle处理的一个应用是更新用户接口对象的状态。更新用户接口状态的内容见消息映射的章节。&空闲处理由函数OnIdle完成,其原型为BOOLOnIdle(int)。参数的含义是当前空闲处理周期已经完成了多少次OnIdle调用,每个空闲处理周期的第一次调用,该参数设为0,每调用一次加1;返回值表示当前空闲处理周期是否继续调用OnIdle。&&MFC的缺省实现里,CWinThread::OnIdle完成了工具栏等的状态更新。如果覆盖OnIdle,需要调用基类的实现。&&在处理完一个消息或进入消息循环时,如果消息队列中没有消息要处理,则MFC开始一个新的空闲处理周期;&&当OnIdle返回FASLE,或者消息队列中有消息要处理时,当前的空闲处理周期结束。&从图5-3中Run的流程上可以清楚的看到MFC空闲处理的情况。&本节描述了应用程序从InitInstance开始初始化、从Run进入消息循环的过程,下面将就SDI应用程序的例子描述该过程中创建各个所需MFC对象的流程。&SDI应用程序的对象创建&如前一节所述,程序从InitInstance开始。在SDI应用程序的InitInstance里,至少有以下语句://第一部分,创建文档模板对象并把它添加到应用程序的模板链表CSingleDocTemplate* pDocTpDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CTDoc),RUNTIME_CLASS(CMainFrame), // main SDI framewindowRUNTIME_CLASS(CTView));AddDocTemplate(pDocTemplate);&//第二部分,动态创建文档、视、边框窗口等MFC对象和对应的Windows对象//Parse command line for standard shellcommands, DDE, file openCCommandLineInfo cmdIParseCommandLine(cmdInfo);// Dispatch commands specified on the commandlineif (!ProcessShellCommand(cmdInfo))return FALSE;&//第三部分,返回TRUE,WinMain下一步调用Run开始消息循环,//否则,终止程序return TRUE;&对于第二部分,又可以分解成许多步骤。下面将解释每一步。&文档模板的创建&第一步是创建文档模板。文档模板的作用是动态创建其他MFC对象,它保存了要动态创建类的动态创建信息和该文档类型的资源ID。这些信息保存在文档模板的成员变量里:m_nIDResource(资源ID)、m_pDocClass(文档类动态创建信息)、m_pFrameClass(边框窗口类动态创建信息)、m_pViewClass(视类动态创建信息)。资源ID包括菜单、像标、快捷键、字符串资源的ID,它们都使用同一个ID值,如IDR_MAINFRAME。其中,字符串资源描述了文档类型,由七个被“\n”分隔的子字符串组成,各个子串可以通过CDocTemplate的成员函数GetDocString(CString&rString, enum DocStringIndexindex)来获取。DocStringIndex是CDocTemplate类定义的枚举变量以区分七个子串,描述如下(英文是枚举变量名称)。WindowTitle 应用程序窗口的标题。仅仅对SDI程序指定。DocName用来构造缺省文档名的字符串。当用File菜单的菜单项new创建新文档时,缺省文档名由该字符串加一个数字构成。如果空,使用“unitled”。FileNewName 文档类型的名称,在打开FileNew对话框时显示。FilterName 匹配过滤字符串,在FileOpen对话框用来过滤要显示的文件。如果不指定,File Open对话框的文件类型(file style)不可访问。FilterExt该类型文档的扩展名。如果不指定,则不可访问对话框的文件类型(File Style)。RegFileTypeId 文档类型在Windows 注册库中的存储标识。RegFileTypeName 文档类型在Windows注册库中的类型名称。&文档模板被应用程序对象创建和管理。应用程序类CWinApp有一个CDocManager类型的成员变量m_pDocManager,通过该变量来管理应用程序的文档模板列表,把一些相关的操作委派给CDocManager对象处理。CDocManager使用CPtrList类型的m_templateList变量来存储文档模板,并提供了操作文档模板列表的系列函数。&从语句pDocTemplate = newCSingleDocTemplate(…)可以看出应用程序对象创建模板时传递一个资源ID和三个类的动态创建信息给它:IDR_MAINFRAME,资源IDRUNTIME_CLASS(CTDoc),文档类动态创建信息RUNTIME_CLASS(CMainFrame),边框窗口类动态创建信息RUNTIME_CLASS(CTView),视类动态创建信息文档模板对象接收这些信息并把它们保存到对应的成员变量里头。然后AddDocTemplate实际调用m_pDocManager-&AddDocTemplate,把创建的模板对象加入到文档模板管理器的模板列表中,也就是应用程序对象的文档模板列表中。&文件的创建或者打开&第二步是创建或者打开文件。对于SDI程序,MFC对象的动态创建过程是在创建或者打开文件中发生的。但是为什么没有看到文件操作相关的语句呢?&CCommandLineInfo&首先,需要弄清楚类CcommandLineInfo,它是用来处理命令行信息的类,CWinApp::PareCommandLine调用CCommandLineInfo的成员函数ParseParm分析启动程序时的参数,把分析结果保存在CCommandLineInfo对象的成员变量里。CCommandLineInfo的定义如下:class CCommandLineInfo : public CObject{BOOL m_bShowSBOOL m_bRunEBOOL m_bRunA&enum { FileNew, FileOpen, FilePrint,FilePrintTo, FileDDE,AppUnregister, FileNothing = -1 }m_nShellC// not valid for FileNewCString m_strFileN// valid only for FilePrintToCString m_strPrinterNCString m_strDriverNCString m_strPortN};由上述定义可以看出,分析结果分几类:是否OLE激活;应该执行什么动作(FileNew、FileOpen等);传递的参数(打开或打印的文件名,打印设备、端口等)。当命令行空时,执行FileNew命令。原因在于CCommandLineInfo的缺省构造函数:CCommandLineInfo::CCommandLineInfo(){m_bShowSplash = TRUE;m_bRunEmbedded = FALSE;m_bRunAutomated = FALSE;m_nShellCommand = FileN//指定了SHELL命令操作}缺省构造把应该执行的动作指定为FileNew。&处理命令行命令&其次,分析CWinApp::ProcessShellCommand(CCommandLineInfo&rCmdInfo)的流程,它处理命令行的命令,流程如图5-3所示。图5-4第三层表示根据命令类型进一步调用的函数,都是CWinApp或者派生类的成员函数。对于FILEDDE类型没有进一步的调用。命令类型是FILENEW时,调用的函数就是标准命令ID_FILE_NEW对应的处理函数OnFileNew;命令类型是FILEOPEN时调用的函数是OpenDocumentFile,标准命令ID_FILE_OPEN的处理函数OnFileOpen的工作实际上就是由OpenDocumentFile完成的。函数FileNew、OpenDocumentFile导致了窗口、文档的创建。&OnFileNew&接着,分析 CWinApp::OnFileNew流程,如图5-5所示。图5-5的说明:应用程序对象得到文档模板管理器指针,调用文档模板管理器的成员函数OnFileNew(m_pDocManager-&OnFileNew());模板管理器获取文档模板对象指针,调用文档模板对象的OpenDocumentFile函数(pTemplate-&OpenDocumentFile(NULL))。如果模板管理器发现有多个文档模板,就弹出一个对话框让用户选择文档模板。这里和后面的图解中类似于CWinApp::、CDocManager::、CDocTemplate::等的函数类属限制并不表示直接源码中有这样的限制,而是通过指针或者指针的动态约束可以认定调用了某个类的成员函数,其正确性仅仅限于本书图解的MFC的缺省实现。如图5-5所示,程序员可以覆盖有关虚拟函数或命令处理函数:如果程序员在自己的应用程序类中覆盖了OnFileNew,则可以实现完全不同的处理流程;一般情况下,不会从文档模板类派生新类,如果派生的话,可以覆盖CDocTemplate的虚拟函数。&OnFileOpen&分析了OnFileNew后,现在分析CWinApp::OnFileOpen(),其流程如图5-6所示。CWinApp::OnFileOpen和OnFileNew类似,不过,第二步须得到一个要打开的文件的名称,第三步调用的是应用程序对象的OpenDocumentFile,而不是文档模板对象的该函数。&应用程序对象的OpenDocumentFile&分析应用程序的打开文档函数: CWinApp::OpenDocumentFile(LPCSTRname),其流程如图5-7所示。 应用程序对象把打开文件操作委托给文档模板管理器,后者又委托给文档模板对象来执行。如果是SDI程序,则委托给单文档对象;如果是MDI程序,则委托给多文档对象──这是由指针所指对象的实际类型决定的,因为该函数是一个虚拟函数。&文档模板的OpenDocumentFile&不论是FileNew还是FileOpen,最后的操作都归结到由文档模板来打开文件(文件名空则创建文件)。CSingleDocTemplate::OpenDocumentFile(lpcstrname,BOOLvisible)的流程见图5-8。有一点需要指出的是:创建了一个文档对象,并不等于打开了一个文档(件)或者创建了一个新文档(件)。图5-8显示的流程大致可以描述如下:如果已经有文档打开,则保存当前的文档;否则,文档对象还没有创建,需要创建一个新的文档对象。因为这时边框窗口还没有生成,所以还要创建边框窗口对象(MFC对象)和边框窗口。MFC边框窗口对象动态创建,HWND边框窗口由LoadFrame创建。MFC边框窗口被创建时,CFrameWnd的缺省构造函数被调用,它把正创建的对象(this所指)加入到模块-线程状态的边框窗口列表m_frameList之首。边框窗口创建过程中由CreateView动态创建MFC视对象和HWND视窗口。接着,如果没有指定要打开的文件名,则创建一个新的文件;否则,则打开文件,并使用序列化机制读入文件内容。通过上述过程,动态地创建了MFC边框窗口对象、视对象、文档对象以及对应的Windows对象,并填写了有关对象的成员变量,建立起这些MFC对象的关系。&打开文件过程中所涉及的消息处理函数和虚拟函数&图5-8描述的整个过程中系列消息处理函数和虚拟函数被调用。例如:在Windwos边框窗口和视窗口被创建时会产生WM_CREATE等消息,导致OnCreate等消息处理函数的调用,CFrameWnd和CView都覆盖了该函数,所以在边框窗口和视窗口的创建中,同样的消息调用了不同的处理函数CFrameWnd::OnCreate和CView::OnCreate。图5-8涉及的几个虚拟函数的流程分别由图5-9、图5-10图解。图5-9表示CDocument的OnNewDocument的流程;图5-10表示CDocument的OpenDocument的流程。这两个函数分别在创建新文档或者打开一个文档时被调用。从流程可以看出,对于OpenDocument函数,MFC的缺省实现主要用来设置修改标识、序列化读入打开文档的内容。图5-10显示了序列化的操作过程:首先,使用文档对象打开或者创建的文件句柄创建一个用于读出数据的CArchive对象loadarchive;然后使用它通过Serialize进行序列化操作,完毕,CArchive对象被自动销毁,文件句柄被关闭。从这些图中可以看到何时、何处调用了什么消息处理函数和虚拟函数,这些函数用来作了什么事情。必要的话,程序员可以在派生类覆盖它们。在创建工作完成之后,进行初始化,使用文档对象的数据来更新视和显示窗口。&至此,本节描述了MFC的SDI程序从分析命令行到创建或打开文件的处理过程,文档对象已经动态创建。总结如下:命令行分析→应用程序的FileNew→文档模板的OpenDocumentFile(NULL)→文档的OnNewDocument命令行分析→应用程序的FileOPen→文档模板的OpenDocumentFile(filename)→文档的OpenDocument边框窗口对象、视对象的动态创建和对应Windows对象的创建从LoadFrame开始,这些将在下一节论述。&&&&SDI边框窗口的创建&第三步是创建SDI边框窗口。图5-8已经分析了创建SDI边框窗口的时机和创建方法,下面,从LoadFrame开始分析整个窗口创建过程。&CFrameWnd::LoadFrame&CFrameWnd::LoadFrame的流程如图5-11所示,其原型如下:BOOL CFrameWnd::LoadFrame(UINT nIDResource,DWORD dwDefaultStyle,CWnd* pParentWnd,CCreateContext* pContext)第一个参数是和该框架相关的资源ID,包括字符串、快捷键、菜单、像标等;第二个参数指定框架窗口的“窗口类”和窗口风格;此处创建SDI窗口时和缺省值相同,为WS_OVERLAPPEDWINDOW |FWS_ADDTOTITLE;第三个参数指定框架窗口的父窗口,此处和缺省值相同,为NULL;第四个参数指定创建的上下文,如图5-8所示由CreateNewFrame生成了该变量并传递给LoadFrame。其缺省值为NULL。创建上下文结构的定义:struct CCreateContext{CRuntimeClass* m_pNewViewC//View的动态创建信息CDocument*m_pCurrentD//指向一文档对象,将和新创建视关联&//用来创建MDI子窗口的信息(CMDIChildFrame::LoadFrame使用)CDocTemplate* m_pNewDocT&// for sharing view/frame state from theoriginal view/frameCView* m_pLastVCFrameWnd* m_pCurrentF};这里,传递给LoadFrame的CCreateContext变量是:(视的动态创建信息,新创建的文档对象,当前文档模板,NULL,NULL)。其中,“新创建的文档对象”就是图5-8中创建的那个文档对象。从此图中还可以看到,LoadFrame被CreateNewFrame调用,CreateNewFrame是文档模板的成员函数,被文档模板的成员函数OpenDocumentFile所调用,所以,LoadFrame间接地被文档模板调用,“当前文档模板”就是调用它的模板对象。顺便指出,对SDI程序来说是这样的,对MDI程序有所不同。“视的动态创建信息”也是文档模板传递过来的。&对图5-11的说明:在创建边框窗口之前,先注册“窗口类”。LoadFrame注册了两个“窗口类”,一个为边框窗口,一个为视窗口。关于“窗口类”注册,见2.2.1节。注册窗口类之后,创建边框窗口,并加载资源。创建边框窗口使用了CFrameWnd的Create虚拟函数,最终调用::CreateEx创建窗口。::CreateEx有11个参数,其最后一个参数就是文档模板传递给LoadFrame的CCreateContext类型的指针,该指针将被传递给窗口过程,进一步由Windows传递给OnCreate函数。顺便指出,创建完毕的边框窗口的窗口过程是统一的MFC窗口过程。创建边框窗口时,发送消息WM_NCCREATE和WM_CREATE,导致对应的消息处理函数OnNcCreate和OnCreate被调用。CWnd提供了OnNcCreate处理非客户区创建消息,CFrameWnd没有处理该消息,但是提供了OnCreate处理消息WM_CREATE。OnCreate将创建视对象和视窗口。&CFrameWnd::OnCreate&按创建工作的进度,现在要讨论边框窗口创建消息(WM_CREATE)的处理了,处理函数是CFrameWnd的OnCreate,其原型如下:int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)其中,参数指向一个CreateStruct结构(关于CreateStruct的描述见4.4.1节),它包含了窗口创建参数的副本,也就是说CreaeEx窗口创建函数的11个参数被对应地复制到该结构的11个域,例如它的第一个成员就可以转换成CCreateContext类型的指针。函数OnCreate处理WM_CREATE消息,它从lpcs指向的结构中分离出lpCreateParams并把它转换成为CCreateContext类型的指针pContext,然后,调用OnCreateHelp(lpcs,pContext),把创建工作委派给它完成。CFrameWnd::OnCreateHelp的原型如下,流程见图5-11。int CFrameWnd::OnCreateHelp(LPCREATESTRUCTlpcs,CCreateContext* pContext)说明:由于CFrameWnd覆盖了消息处理函数OnCreate来处理WM_CREATE消息,所以CWnd就失去了处理该消息的机会,除非CFrameWnd::OnCreate主动调用基类的该消息处理函数。图5-11展示了对CWnd::OnCreate的调用。在边框窗口被创建之后,调用虚拟函数OnCreateClient(lpcs,pContext),它的缺省实现将创建视对象和视窗口。最后,在状态栏显示“Ready”字样,调用RecalcLayout安排工具栏等的位置。关于WM_SETMESSAGESTRING消息和RecalcLayout函数,见工具栏有关13.2.3节。到此,SDI的边框窗口已经被创建。下一节将描述视的创建。&视的创建&第四步,创建视。如前一节所述,若CFrameWnd::OnCreateClient(lpcs,pContext)判断pContext包含了视的动态创建信息,则调用函数CreateView创建视对象和视窗口。CreateView的原型如下,其流程如图5-13所示。CWnd * CFrameWnd::CreateView(CCreateContext*pContext, UINT nID)其中:第一个参数是创建上下文;第二个参数是创建视(子窗口)的ID,缺省是AFX_IDW_PANE_FIRST,这里等同缺省值。说明:CreateView调用了CWnd的Create函数创建HWND视窗口,视的子窗口ID是AFX_IDW_PANE_FIRST,父窗口是创建它的边框窗口。创建视窗口时的WM_CREATE、WM_NCCREATE消息分别被CView、CWnd的相关消息处理函数处理。处理情况如图5-13所述,这里不作进一步的讨论。到此,文档对象、边框窗口对象、视窗口对象已经创建,文件已经打开或者创建,边框窗口、视窗口已经创建。现在,是显示和定位窗口、显示文档数据的时候了,这些通过调用CFrameWnd的虚拟函数InitialUpdateFrame完成,如图5-8所示。&窗口初始化&这是第五步,初始化边框窗口、视窗口等。InitialUpdateFrame的原型如下:void CFrameWnd::InitialUpdateFrame(CDocument*pDoc, BOOL bMakeVisible)其中:第一个参数是当前的文档对象;第二个参数表示边框窗口是否应该可见并激活。该函数是在文档模板的OpenDocumentFile中调用的,传递过来的第一个参数是刚才创建的文档,第二个参数是TRUE,见图5-8。InitialUpdateFrame的处理过程参见图5-14,解释如下:首先,如果当前边框窗口没有活动视,则获取ID为AFX_IDW_PANE_FIRST的视pView。如果该视不存在,则pView=NULL;否则(pView!=NULL),调用成员函数SetActiveView(pView,FALSE)把它设置为活动视,参数2为FALSE表示并不通知它成为活动视(见图5-14)。然后,如果InitialUpdateFrame的参数bMakeVisible为TRUE,则给所有边框窗口的视发送WM_INITIALUPDATE消息,通知它们在显示之前使用文档数据来初始化视。这导致视类的虚拟函数OnInitUpdate被调用,该函数又调用OnUpdate来完成初始化。其他子窗口(如状态栏、工具栏)也将收到WM_INITIALUPDATE消息,导致它们更新状态。其三,调用pView-&OnActivateFrame(WA_INACTIVE,this)给活动视(若存在的话)一个机会来保存当前焦点。这里,解释这个函数:void CView::OnActivateFrame( UINTnState,CFrameWnd* pFrameWnd );其中,参数1取值为WA_INACTIVE/WA_ACTIVE/WA_CLICKACTIVE,具体见消息WM_ACTIVE的解释;参数2指向被激活的框架窗口。视对象通过该虚拟函数在它所属的边框窗口被激活或者失去激活时作一些特别的处理,例如,CFormView用它来保存或者恢复输入焦点控制。其四,在OnActivateFrame之后,调用成员函数ActivateFrame激活框架窗口。这个过程将产生一个消息WM_ACTIVE(处理该消息的过程在下一节作解释),它导致OnActiveTopLevel和OnActive被调用。接着,如果活动视非空,则调用成员函数OnActivateView激活它。至此,参数bMakeVisible为TRUE时显示窗口的处理完毕。最后,如果参数pDoc非空,则更新边框窗口计数,更新边框窗口的标题。更新边框窗口计数是为了在一个文档对应多个视的情况下,给显示同一文档的不同文档边框窗口编号,编号从1开始,保存在边框窗口的成员变量m_nWindow里。例如有两个边框对应一个文档tt1,则它们的标题分别为“tt1:1”、“tt1:2”。如果只有一个文档只对应一个边框窗口,则成员变量m_nWindow等于-1,标题不含编号,如“tt1”。当然,对于SDI应用程序,不存在一个文档对应多个视的情况。上述情况是针对MDI应用程序而言的。SDI应用程序执行该过程时,相当于MDI程序的一个特例。图 5-14涉及的一些函数由图5-15、5-15图解。& 图5-14中的函数SetActiveView的图解如图5-15所示,其原型如下,:void CFrameWnd::SetActiveView(CView * pViewNew,BOOL bNotify = TRUE)其中:参数1指向被设置的视对象,若非视类型的对象,则为NULL;参数 2表示是否通知被设置的视。图5-15中的变量m_pViewActive是CFrameWnd的成员变量,用来保存边框窗口的活动视。图5-15中的流程可以概括为:Deactivate当前视(m_pViewActive非空时);设置当前活动视;若参数bNotify为TRUE,通知pViewNew被激活。&图5-14中的函数ActivateFrame图解如图5-16所示,其原型如下,:void CFrameWnd::ActivateFrame(UINT nCmdShow)参数nCmdShow用来传递给CWnd::ShowWindow,指定显示窗口的方式。参数缺省为1,图5-14调用时设置为-1。该函数用来激活(Activate)和恢复(Restore)边框窗口,使得它对用户可见可用。在初始化、OLE事件、DDE事件等需要显示边框窗口的地方调用。图5-16表示的MFC缺省实现是激活边框窗口并把它放到顶层。程序员可以覆盖该虚拟函数ActivateFrame来控制边框窗口怎样被激活。图5-16中的函数BringToTop是CFrameWnd内部使用的成员函数(protected)。它调用::BringWindowToTop把窗口放到Z轴上的顶层。至此,边框窗口初始化的过程已经描述清楚,视的初始化见下一节。&视的初始化&第六步,在边框窗口初始化之后,初始化视。如图5-14所示,视、工具条窗口处理消息WM_INITAILUPDATE(MFC内部消息),完成初始化。这里只讨论视的消息处理函数,其原型如下:void CView::OnInitialUpdate()图5-14对该函数的注释说明了该函数的特殊之处。其缺省实现是调用OnUpdate(NULL,0, NULL)更新视。可以覆盖OnInitialUpdate实现自己的初始化。&OnUpdate是一个虚拟函数,其原型如下:void CView::OnUpdate(CView* pSender, LPARAMlHint, CObject* pHint)其中:参数1指向修改文档数据的视;若更新所有的视,设为NULL;参数2是一个包含了修改信息的long型变量;参数3指向一个包含修改信息的对象(从CObject派生的对象)。参数2、参数3是在文档更新对应视的时候传递过来的。该函数用来更新显示视窗口,反映文档的变化,在MFC中,它为函数CView::OnInitialUpdate和CDocument::UpdateAllViews所调用。其缺省实现是使整个客户区无效。在下一次收到WM_PAINT消息时,重绘无效区。工具条的初始化见讨论第13章。&激活边框窗口(处理WM_ACTIVE)&第七步,在窗口初始化完成之后,激活并显示出来。下面讨论边框窗口激活时的处理(对WM_ACTIVE的处理)。&WM_ACTIVE的消息参数&wParam的低阶word指示窗口是被激活还是失去激活:WA_ACTIVE,被鼠标点击以外的方法激活;WA_CLICKACTIVE,由鼠标点击激活;WA_INACTIVE,失去激活;wParam的高阶word指示窗口是否被最小化;非零表示最小化;lPararm表示将激活的窗口句柄(WA_INACTIVE),或者将失去激活的窗口句柄(WA_CLICKACTIVE、WA_ACTIVE)。&在标准Windows消息处理的章节中,曾指出处理WM_ACTIVE消息时,先要调用一个函数_AfxHandleActivate,此函数的原型如下:static void AFXAPI _AfxHandleActivate(CWnd*pWnd,WPARAM nState,CWnd* pWndOther)其中:参数1是接收消息的窗口;参数2是窗口状态,为WM_ACTIVE的消息参数wP参数3是WM_ACTIVE的消息参数lParam表示的窗口。_AfxHandleActivate是MFC内部使用的函数,声明和实现均在WinCore.CPP文件中。实现如下:如果pWnd指向的窗口不是子窗口,而且pWnd和pWndOther窗口的顶级父窗口(TopLevelParent)不是同一窗口,则发送MFC定义的消息WM_ACTIVATETOPLEVEL给pWnd的顶级窗口,消息参数wParam是nState,消息参数lParam指向一个长度为二的数组,数组里存放pWnd和pWndOther所指窗口的句柄。否则,_AfxHandleActivate不作什么。从这里可以看出:只有顶层的主边框窗口能处理WM_ACTIVE消息,事实上,Windows系统只会给顶层的非子窗口发送WM_ACTIVE消息。&WM_ACTIVATETOPLEVEL消息的处理&CWnd及派生类CFrameWnd实现了对WM_ACTIVATETOPLEVEL消息的处理,分别解释如下:消息处理函数CWnd::OnActivateTopLevel如果失去激活,则取消工具栏的提示(TOOLTIP)。消息处理函数CFrameWnd::OnActivateTopLevel调用CWnd的OnActivateTopLevel;如果接收WM_ACTIVE消息的窗口是线程主窗口,则使得其活动的视窗口变成非活动的(OnActive(FALSE,pActiveView,pActiveView)。从这里可以知道,在顶层窗口接收到WM_ACTIVE消息后,MFC会进行一些固定的处理,然后才调用WM_ACTIVE消息处理函数。&WM_ACTIVE消息的处理&在_AfxHandleActivate和WM_ACTIVATETOPLEVEL消息处理完之后,才是对WM_ACTIVE的处理。CWnd和CFrameWnd都实现了消息处理。CWnd的消息处理函数:void CWnd::OnActive(UINT nState, CWnd*pWndOther, BOOL bMinimized)其中:参数1取值为WA_INACTIVE/WA_ACTIVE/WA_CLICKACTIVE;参数2指向激活或者失去激活的窗口,具体同WM_ACTIVE消息;参数3表示是否最小化。此函数的实现是调用Default(),作缺省处理。&CFrameWnd的消息处理函数:void CFrameWnd::OnActive(UINT nState,CWnd*pWndOther, BOOL bMinimized)首先调用CWnd::OnActivate。如果活动视非空,消息是WA_ACTIVE/WA_CLICKACTIVE,并且不是最小化,则重新激活当前视,调用了以下函数:pActiveView-&OnActiveView(TRUE,pActiveView,pActiveView);并且,如果活动视非空,通知它边框窗口状态的变化(激活/失去激活),调用以下函数:pActiveView-&OnActivateFrame(nState, this)。&SDI流程的回顾&从InitialInstance开始,首先应用程序对象创建文档模板,文档模板创建文档对象、打开或创建文件;然后,文档模板创建边框窗口对象和边框窗口;接着边框窗口对象创建视对象和视窗口。这些创建是以应用程序的文档模板为中心进行的。在创建这些MFC对象的同时,建立了它们之间的关系。创建这些之后,进行初始化,激活主边框窗口,把边框窗口、视窗口显示出来。这样,一个SDI应用程序就完成了启动过程,等待着用户的交互或者输入。5.3.4节将在SDI程序启动流程的基础之上,介绍MDI应用程序的启动流程。两者的相同之处可以这样类比:创建SDI边框窗口、视、文档的过程和创建MDI文档边框窗口、视、文档的过程类似。不同之处主要表现在:主边框窗口的创建不一样;MDI有文档边框窗口的创建,SDI没有;SDI只能一个文档、一个视;MDI可能多文档、多个视。&MDI程序的对象创建&MDI应用程序对象的InitialInstance函数一般含有以下代码://第一部分:创建和添加模板CMultiDocTemplate* pDocTpDocTemplate = new CMultiDocTemplate(IDR_TTTYPE,RUNTIME_CLASS(CTtDoc),RUNTIME_CLASS(CChildFrame),//custom MDI childframeRUNTIME_CLASS(CTtView));AddDocTemplate(pDocTemplate);&//第二部分:创建MFC框架窗口对象和Windows主边框窗口// 创建主MDI边框窗口CMainFrame* pMainFrame = new CMainFif(!pMainFrame-&LoadFrame(IDR_MAINFRAME))return FALSE;m_pMainWnd = pMainF&//第三部分:处理命令行,命令行空则执行OnFileNew创建新文档//分析命令行CCommandLineInfo cmdIParseCommandLine(cmdInfo);// 处理命令行命令if (!ProcessShellCommand(cmdInfo))return FALSE;&第四部分:显示和更新主框架窗口// 主窗口已被初始化,现在显示和更新主窗口pMainFrame-&ShowWindow(m_nCmdShow);pMainFrame-&UpdateWindow();&SDI应用程序对象的InitialInstance和SDI应用程序对象的InitialInstance比较,有以下的相同和不同之处。相同之处在于:创建和添加模板;处理命令行。不同之处在于:&创建的模板类型不同。SDI使用单文档模板,边框窗口类从CFrameWnd派生;MDI使用多文档模板,边框窗口类从CMDIChildWnd派生.&&主窗口类型不同。SDI的是从CFrameWnd派生的类;MDI的是从CMDIFrameWnd派生的类。&&主框架窗口的创建方式不同。SDI在创建或者打开文档时动态创建主窗口对象,然后加载主窗口(LoadFrame)并初始化;MDI使用第二部分的语句来创建动态主窗口对象和加载主窗口,第四部分语句显示、更新主窗口。&&命令行处理的用途不一样。SDI一定要有命令行处理部分的代码,因为它导致了主窗口的创建;MDI可以去掉这部分代码,因为它的主窗口的创建、显示等由第二、四部分的语句来处理。&&有别于SDI的主窗口加载过程&和SDI应用程序一样,MDI应用程序使用LoadFrame加载主边框窗口,但因为LoadFrame的虚拟属性,所以MDI调用了CMDIFrameWnd的LoadFrame函数,而不是CFrameWnd的LoadFrame。LoadFrame的参数1指定了资源ID,其余几个参数取缺省值。和SDI相比,第四个创建上下文参数为NULL,因为MDI主窗口不需要文档、视等的动态创建信息。图 5-17图解了CMdiFrameWnd::LoadFrame的流程:&首先,用同样的参数调用基类CFrameWnd的LoadFrame,其流程如图5-11所示,但由于参数4表示的创建上下文为空,所以,CFrameWnd::LoadFrame在加载了菜单和快捷键之后,给所有子窗口发送WM_INITUPDATE消息。另外,WM_CREATE消息怎样处理呢?由于CMDIFrameWnd没有覆盖OnCreate,所以还是由基类CFrameWnd::OnCreate处理。但是它调用虚拟函数OnCreateClient(见图5-12)时,由于CMDIFrameWnd覆盖了该函数,所以动态约束的结果是CMDIFrameWnd::OnCreateClient被调用,它和基类的OnCreateClient不同,后者CreateView创建MFC视对象和视窗口,前者调用虚拟函数CreateClient创建MDI客户窗口。MDI客户窗口负责创建和管理MDI子窗口。&CreateClient是CMDIFrameWnd的虚拟函数,其原型如下:BOOL CMDIFrameWnd::CreateClient(LPCREATESTRUCT lpCreateStruct, CMenu*pWindowMenu);该函数主要用来创建MDI客户区窗口。它使用Windows系统预定义的“mdiclient”窗口类来创建客户区窗口,保存该窗口句柄在CMDIFrameWnd的成员变量m_hWndMDIClient中。调用::CreateWindowEx创建客户窗口时传递给它的窗口创建数据参数(第11个参数)是一个CLIENTCREATESTRUCT结构类型的参数,该结构指定了一个菜单和一个子窗口ID:typedef struct tagCLIENTCREATESTRUCT{HMENU hWindowMUINT idFirstC}CLIENTCREATESTRUCT;hWindowMenu表示主边框窗口菜单栏上的“Windows弹出菜单项”的句柄。MDICLIENT类客户窗口在创建MDI子窗口时,把每一个子窗口的标题加在这个弹出菜单的底部。idFirstChild是第一个被创建的MDI子窗口的ID号,第二个MDI子窗口ID号为idFirstChild+1,依此类推。这里,hWindowMenu的指定不是必须的,程序员可以在MDI子窗口激活时进行菜单的处理;idFirstChild的值是AFX_IDM_FIRST_MDICHILD。综合地讲,CMDIFrameWnd::LoadFrame完成创建MDI主边框窗口和MDI客户区窗口的工作。创建了MDI边框窗口和客户区窗口之后,接着是处理WM_INITUPDATE消息,进行初始化。但是按SDI应用程序的讨论顺序,下一节先讨论MDI子窗口的创建。&MDI子窗口、视、文档的创建&和SDI应用程序类似,MDI应用程序通过文档模板来动态创建MDI子窗口、视、文档对象。不同之处在于:这里使用了多文档模板,调用的是CMDIChildWnd(或派生类)的消息处理函数和虚拟函数,如果它覆盖了CFrameWnd的有关函数的话。还是以处理标准命令消息ID_FILE_NEW的OnFileNew为例。表示OnFileNew的图5-5、表示OnFileOpen的图5-6在多文档应用程序中仍然适用,但表示OpenDocumentFile的图5-8有所不同,其第三步中地单文档模板应当换成多文档模板,关于这一点,参阅图5-8的说明。(1)多文档模板的OpenDocumentFileMDI的OpenDocumentFile的原型如下:CDocument*CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible);它的原型和单文档模板的该函数原型一样,但处理流程比图5-8要简单些:第一,不用检查是否已经打开了文档;第二,不用判断是否需要创建框架窗口或者文档对象,因为不论新建还是打开文档都需要创建新的文档框架窗口(MDI子窗口)和文档对象。除了这两点,其他处理步骤基本相同,调用同样名字的函数来创建文档对象和MDI子窗口。虽然是名字相同的函数,但是参数的值可能有异,又由于C++的虚拟机制和MFC消息映射机制,这些函数可能来自不同层次类的成员函数,因而导致有不同的处理过程和结果,即SDI创建了CFrameWnd类型的对象和边框窗口;MDI则创建了CMDIChildWnd类型的对象和边框窗口。不同之处解释如下:(2)CMDIChildWnd的虚拟函数LoadFrameCMDIChildWnd::LoadFrame代替了图5-8中的CFrameWnd::LoadFrame,两者流程大致相同,可以参见图5-11。但是它们用来创建窗口的函数不同。前者调用了函数CMDIChildWnd::Create(参数1…参数6);后者调用了CFrameWnd::Create(参数1…参数7)。这两个窗口创建函数,虽然都是虚拟函数,但是有很多不同之处:&前者是CMDIChildWnd定义的虚拟函数,后者是CWnd定义的虚拟函数;&&前者在参数中指定了父窗口,即主创建窗口,后者的父窗口参数为NULL;&&前者指定了WS_CHILD风格,创建的是子窗口,后者创建一个顶层窗口;&&前者给客户窗口m_hWndMDIClient(CMDIFrameWnd的成员变量)发送WM_MDICREATE消息让客户窗口来创建MDI子窗口(主边框窗口的子窗口是客户窗口,客户窗口的子窗口是MDI子窗口),后者调用::CreateEx函数来创建边框窗口;&&前者的窗口创建数据是指向MDICREATESTRUCT结构的指针,该结构的最后一个域存放一个指向CCreateContext结构的指针,后者是指向CCreateContext结构的指针。&MDICREATESTRUCT结构的定义如下:typedef struct tagMDICREATESTRUCT { // mdicLPCTSTR szCLPCTSTR szTHANDLE hODWORDLPARAM lP}MDICREATESTRUCT;该结构的用处和CREATESTRUCT类似,只是它仅用于MDI子窗口的创建上,用来保存创建MDI子窗口时的窗口创建数据。域lParam保存一个指向CCreateContext结构的指针。&WM_CREATE的处理函数不同&创建MDI子窗口时发送的WM_CREATE消息由CMDIChildWnd的成员函数OnCreate(LPCREATESTRUCTlpCreateStruct)处理。OnCreate函数仅仅从lpCreateStruct指向的数据中取出窗口创建数据,即指向MDICREATESTRUCT结构的指针,并从该结构得到指向CCreateContext结构的指针pContext,然后调用虚拟函数OnCreateHelper(lpCreateStruct,pContext)。此处动态约束的结果是调用了CFrameWnd的成员函数OnCreateHelper。SDI应用程序的OnCreate也调用了CFrameWnd::OnCreateHelper,所以后面的处理(创建视等)可参见SDI的流程了。待MDI子窗口、视、文档对象创建完毕,多文档模板的OpenDocumentFile也调用InitialUpdateFrame来进行初始化。&MDI子窗口的初始化和窗口的激活&(1)MDI子窗口的初始化完成了MDI子窗口、视、文档的创建之后,多文档模板的OpenDocumenFile调用边框窗口的虚拟函数InitialUpdateFrame进行初始化,该函数流程参见图5-14。不过,这里this指针指向CMDIChildWnd对象,由于C++虚拟函数的动态约束,初始化过程调用了CMDIChildWnd的ActivateFrame函数(不是CFrameWnd的ActivateFrame),来显示MDI子窗口,更新菜单等等,见图5-18。图5-18的说明:第一,调用基类CFrameWnd的ActivateFrame显示窗口时,由于当前窗口是文档边框窗口,所以没有发送WM_ACTIVATE消息,而是发送消息WM_MDIACTIVATE。第二,由于Windows不处理MDI子窗口的激活,所以必须由MFC或者程序员来完成。当一个激活的MDI子窗口被隐藏后从可见变成不可见,但它仍然是活动的,这时需要把下一文档边框窗口激活以便用户看到的就是激活的窗口。在没有其他文档边框窗口时,则把该隐藏的文档边框窗口标记为“伪失去激活”。当一个文档边框窗口从不可见变成可见时,检查变量m_bPseudoInactive,若真则该窗口从Windows角度看仍然是激活的,只需要调用OnMDIActivate把它改成“MFC激活”。OnMDIActivate把变量m_bPseudoInactive的值改变为FALSE。至此,MDI子窗口初始化调用描述完毕。初始化将导致MDI窗口被显示、激活。下面讨论MDI子窗口的激活。(2)MDI子窗口的激活通过给客户窗口发送消息WM_MDIACTIVATE来激活文档边框窗口。客户窗口发送WM_MDIACTIVATE消息给将被激活或者取消激活的MDI子窗口(文档边框窗口),这些子窗口调用消息处理函数OnMDIActivate响应该消息WM_MDIACTIVATE。关于MDI消息,见表5-12。用户转向一个子窗口(包括文档边框窗口)导致它的顶层(TOPLEVEL)边框窗口收到WM_ACTIVATE消息而被激活,子窗口是文档边框窗口的话将收到WM_MDIACTIVATE消息。但是,一个边框窗口被其他方式激活时,它的文档边框窗口不会收到WM_MDIACTIVATE消息,而是最近一次被激活的文档边框窗口收到WM_NCACTIVATE消息。该消息由CWnd::Default缺省处理,用来重绘文档边框窗口的标题栏、边框等等。&MDI子窗口用OnMDIActiveate函数处理WM_MDIACTIVATE消息。其原型如下:void CMDIChildWnd::OnMDIActivate( BOOLbActivate,CWnd* pActivateWnd,CWnd* pDeactivateWnd );其中:参数1表示是激活(TRUE),还是失去激活(FALSE);参数2表示将被激活的MDI子窗口;参数3表示将被失去激活的MDI子窗口;简单地说,该函数把m_bPseudoInactive的值改变为FALSE,调用成员函数OnActivateView通知失去激活的子窗口的视它将失去激活,调用成员函数OnActivateView通知激活子窗口的视它将被激活。&至于MDI主边框窗口,它还是响应WM_ACTIVATE消息而被激活或相反。CMDIFrameWnd没有提供该消息的处理函数,它调用基类CFrameWnd的处理函数OnActivate。现在,MDI应用程序的启动过程描述完毕。表5-12 MDI消息&消息说明WM_MDIACTIVATE激活MDI Child窗口WM_MDICASCADECASCADE排列MDI Child窗口WM_MDICREATE创建MDI Child窗口WM_MDIDESTROY销毁MDI Child窗口WM_MDIGETACTIVE得到活动的MDI Child窗口WM_MDIICONARRANGE安排最小化了的MDI Child窗口WM_MDIMAXIMIZEMDI Child窗口最大化WM_MDINEXT激活Z轴顺序的下一MDI Child窗口WM_MDIREFRESHMENU根据当前MDI Child窗口更新菜单WM_MDIRESTORE恢复MDI Child窗口WM_MDISETMENU根据当前MDI Child窗口设置菜单WM_MDITITLETITLE安排MDI Child窗口&应用程序的退出&一个Windows应用程序启动之后,一般是进入消息循环,等待或者处理用户的输入,直到用户关闭应用程序窗口,退出应用程序为止。例如,用户按主窗口的关闭按钮,或者选择执行系统菜单“关闭”,或者从“文件”菜单选择执行“退出”,都会导致主窗口被关闭。当用户从“文件”菜单选择执行“退出”时,将发送MFC标准命令消息ID_APP_EXIT。MFC实现了函数CWinApp::OnAppExit()来完成对该命令消息的缺省处理。void CWinApp::OnAppExit(){// same as double-clicking on main window closeboxASSERT(m_pMainWnd != NULL);m_pMainWnd-&SendMessage(WM_CLOSE);}可以看出,其实现是向主窗口发送WM_CLOSE消息。主窗口处理完WM_CLOSE消息之后,关闭窗口,发送WM_QUIT消息,退出消息循环(见图5-3),进而退出整个应用程序。&边框窗口对WM_CLOSE的处理&MFC提供了函数CFrameWnd::OnClose来处理各类边框窗口的关闭:不仅包括SDI的边框窗口(从CFrameWnd派生),而且包括MDI的主边框窗口(从CMDIFrameWnd派生)或者文档边框窗口(从CMDIChildWnd派生)的关闭。该函数的原型如下,流程如图6-1所示:void CFrameWnd::OnClose()从图6-1中可以看出,它首先判断是否可以关闭窗口(m_lpfnCloseProc是函数指针类型的成员变量,用于打印预览等情况下),然后,根据具体情况进行处理:&如果是主窗口被关闭,则关闭程序的所有文档,销毁所有窗口,退出程序;&&如果不是主窗口被关闭,则是文档边框窗口被关闭,又分两种情况:若该窗口所显示的文档被且仅被该窗口显示,则关闭文档和文档窗口并销毁窗口;若该窗口显示的文档还被其他文档边框窗口所显示,则仅仅关闭和销毁文档窗口。&& 下面是处理 WM_CLOSE消息中涉及的一些函数。&BOOL CDocument::SaveModified()&该虚拟函数的缺省实现:首先调用IsModifed判断文档是否被修改,没有修改就返回,否则继续。当询问用户是否保存被修改的文档时,若用户表示“cancel”,返回FALSE;若用户表示“no”,则返回TRUE;若用户表示“yes”,则存盘失败返回FALSE,存盘成功返回TRUE。存盘处理首先要得到被保存文件的名称,然后调用虚拟函数OnSaveDocument完成存盘工作,并使用SetModifidFlag(FALSE)设置文档为没有修改。&&BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)&该函数是虚拟函数,用来保存文件。其实现的功能和OpOpenDocument相反,但处理流程类似,描述如下:根据lpszPathName打开文件pFile;使用pFile构造一个用于写入数据的CArchive对象,此对象用来保存数据到文件;设置鼠标为时间瓶形状;使用Serialize函数完成序列化写;完毕,恢复鼠标的形状。&&CWinApp::SaveAllModified()&CWinApp::CloseAllDocuments(BOOL bEndSession)这两个函数都遍历模板管理器列表,并分别对列表中的模板管理器对象逐个调用CDocManager的同名成员函数:CDocManager::SaveAllModified()CDocManager::CloseAllDocuments(BOOLbEndSession)这两个函数都遍历其文档模板列表,并分别对列表中的模板对象逐个调用CDocTemplate的同名成员函数:CDocTemplate::SaveAllModified()CDocTemplate::CloseAllDocuments(BOOLbEndSession)这两个函数都遍历其文档列表,并分别对列表中的文档对象逐个调用CDocuemnt的成员函数:CDocument::SaveModified()CDocument::OnCloseDocument()&&CDocument::SaveModified()&CDocument::OnCloseDocument()CDocument::SaveModified前面已作了解释。OnCloseDocument是一个虚拟函数,其流程如图6-2所示。通过文档对象所对应的视,得到所有显示该文档的边框窗口的指针:在SDI程序关闭窗口时,获取的是主边框窗口;在MDI程序关闭窗口时,获取的是MDI子窗口。然后,关闭并销毁对应的边框窗口。如果文档对象的 m_bAutoDelete为真,则销毁文档对象自身。 &窗口的销毁过程&&DestroyWindow&从图6-1、图6-2可以看出,销毁窗口是通过调用DestroyWindow来完成的。DestroyWindow是CWnd类的一个虚拟函数。CWnd实现了该函数,而CMDIChildWnd覆盖了该函数。(1)CWnd::DestroyWindow()主要就是调用::DestroyWindow销毁m_hWnd(必须非空),同时销毁其菜单、定时器,以及完成其他清理工作。::DestroyWindow使将被销毁的窗口失去激活、失去输入焦点,并发送WM_DESTROY、WM_NCDESTROY消息到该窗口及其各级子窗口。如果被销毁的窗口是子窗口且没有设置WM_NOPARENTNOTFIY风格,则给其父窗口发送WM_PARENTNOFITY消息。(2)CMDIChildWnd::DestroyWindow()因为MDI子窗口不能使用::DestroyWindows来销毁,所以CMdiChildWnd覆盖了该函数,CMDIChildWnd主要是调用成员函数MDIDestroy给客户窗口(父窗口)发送消息WM_MDIDESTROY,让客户窗口来销毁自己。&处理WM_DESTROY消息&消息处理函数OnDestroy处理WM_DESTROY消息,CFrameWnd、CMDIChildWnd、CWnd、CView及其派生类(如CEditView等等)、CControlBar等都提供了对该消息的处理函数。这里,主要解释边框、文档边框、视类的消息处理函数OnDestroy。&CWnd::OnDestroy()&调用缺省处理函数Default()。&CFrameWnd::OnDestroy()&首先,销毁工具栏的窗口;然后,设置菜单为缺省菜单;接着,如果要销毁的是主边框窗口,则通知HELP程序本应用程序将退出,没有其他程序使用WINHELP则关闭WINHELP;最后调用CWnd::OnDestroy。&CMDIFrameWnd::OnDestroy()&首先,调整客户窗口的边界类型;然后,调用基类CframeWnd的OnDestroy。这时,MDI子窗口的工具栏窗口列表为空,故没有工具栏窗口可以销毁。&CView::OnDestroy()&首先,判断自身是否是边框窗口的活动视,如果是则调用边框窗口的SetActivateView使自己失去激活;然后,调用基类CWnd的OnDestroy。&处理WM_NCDESTROY消息&窗口的非客户区被销毁时,窗口接收WM_NCDESTROY消息,由OnNcDestroy处理WM_NCDESTROY消息。在MFC中,OnNcDestroy是Windows窗口被销毁时调用的最后一个成员函数。CWnd、CView的某些派生类提供了对该消息的处理函数,这里只讨论CWnd的实现。&CWnd::OnNcDestroy()&首先判断当前线程的主窗口是否是该窗口,如果是且模块非DLL,则发送WM_QUIT消息,使得程序结束;然后,判断当前线程的活动窗口是否是该窗口,如果是则设置活动窗口为NULL;接着,清理Tooltip窗口,调用Default由Windows缺省处理WM_NCDESTROY消息,UNSUBCLASS,把窗口句柄和MFC窗口对象分离(Detach);最后,调用虚拟函数PostNcDestoy。&PostNcDestoy&CWnd、CFrameWnd、CView、CControlBar等都覆盖了该函数。文档边框窗口和边框窗口都使用CFrameWnd::PostNcDestroy。&CWnd::PostNcDestroy()&MFC缺省实现空&void CFrameWnd::PostNcDestroy()&调用delete this销毁自身这个MFC对象。&void CView::PostNcDestroy()&调用delete this销毁自身这个MFC对象。&析构函数&deletethis导致析构函数的调用。需要提到的是CFrameWnd和CView的析构函数。&CFrameWnd::~CFrameWnd()&边框窗口在创建时,把自身加入到模块-线程状态的边框窗口列表m_frameList中。现在,从列表中移走该窗口对象。必要的话,删除m_phWndDisable数组。&CView::~CView()&在视创建时,把自身加入到文档对象的视列表中。现在,从列表中移走该视对象。应用程序调用CloseAllDocument关闭文档时。参数为FALSE,它实际上并没有把视从列表中清除,而最后的清除是由析构函数来完成的。&至此,边框窗口关闭的过程讨论完毕。下面,结合具体情况──SDI窗口的关闭、MDI主窗口的关闭、MDI子窗口的关闭──描述对WM_CLOSE消息的处理。&SDI窗口、MDI主、子窗口的关闭&参考图6-1分析SDI窗口、MDI主、子窗口的关闭流程。&SDI窗口的关闭&在这种情况下,主窗口将被关闭。首先,关闭应用程序的文档对象。文档对象的虚拟函数OnCloseDocument调用时销毁了主窗口(Windows窗口和MFC窗口对象),同时也导致视、工具条窗口的销毁。主窗口销毁后,应用程序的主窗口对象为空,故发送WM_QUIT消息结束程序。&MDI主窗口的关闭&首先,关闭应用程序的所有文档对象。文档对象的OnCloseDocument函数关闭文档时,将销毁文档对象对应的文档边框窗口和它的视窗口。这样,所有的MDI子窗口(包括其子窗口视)被销毁,但应用程序的主窗口还在。接着,调用DestroyWindow成员函数销毁主窗口自身,DestroyWindow发现被销毁的是应用程序的主窗口,于是发送WM_QUIT消息结束程序。&MDI子窗口(文档边框窗口)的关闭&在这种情况下,被关闭的不是主窗口。判断与该文档边框窗口对应的文档对象是否还被其他一个或者多个文档边框窗口使用,如果是,则仅仅销毁该文档边框窗口(包括其子窗口视);否则,关闭文档,文档对象的OnCloseDocument将销毁该文档边框窗口(包括其子窗口视)。所属分类:→&&&&作者:新浪博客&&&&时间: 0:00:00
All Right Reserved}

我要回帖

更多关于 c void 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信