MFC教程(5)-- MFC对象的创建(1)

来源:互联网 发布:mysql修改密码 编辑:程序博客网 时间:2024/06/14 20:31
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对象

  视列表

  GetFirstViewPosition

  GetNextView

  文档模板

  GetDocTemplate

  CView对象

  文档对象

  GetDocument

  边框窗口

  GetParentFrame

  CMDIChildWnd或

  CFrameWnd对象

  活动视

  GetActiveView

  活动视的文档

  GetActiveDocument

  CMDIFrameWnd对象

  活动文档边框窗口

  MDIGetActive

  表5-3 从一个对象通知另一个对象的方法:

  本对象

  要通知的对象/动作

  使用的成员函数

  CView对象

  通知文档更新所有视

  CDocument::UpdateAllViews

  CDocument对象

  更新一个视

  CView::OnUpdate

  CFrameWnd或

  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类的虚拟函数接口

  类

  虚拟函数

  覆盖的目的和功能

  CCmdTarget

  OnCmdMsg

  发送、派发命令消息

  OnFinalRelease

  OLE用途,引用为0时作清理工作

  CWinThread

  ExitInstance

  在线程退出时作清理工作

  InitInstance

  在线程开始时作初始化

  OnIdle

  执行thread-specific idle-time处理

  PreTranslateMessage

  在消息送给Windows函数TranslateMessage and DispatchMessage.之前进行消息过滤

  IsIdleMessage

  检查是否是某个特别的消息

  ProcessWndProcException

  截获线程消息/命令处理中的例外

  ProcessMessageFilter

  线程消息过滤

  Run

  实现线程特定的消息循环

  CWinApp

  HideApplication

  关闭所有的窗口之前隐藏应用程序

  CloseAllDocument

  退出程序之前关闭所有文档

  转下页

  续表

 

  SaveModifiedDocument

  框架窗口关闭时用来保存文档

  DoMessageBox

  实现客户化的messagebox

  DoWaitCursor

  关闭或打开等待光标

  OnDDeCommand

  响应DDE命令

  WinHelp

  调用WinHelp函数

  CWnd

  WindowProc

  提供一个窗口过程

  DefWindowProc

  为应用程序不处理的消息提供缺省处理

  PostNcDestroy

  在窗口销毁之后被消息处理函数OnNcDestroy调用

  OnNotify

  处理通知消息WM_NOTIFY

  OnChildNotify

  父窗口调用它给控制子窗口一个机会来处理通知反射消息

  DoDataExchange

  Updata调用它来进行对话框数据交换和验证

  CFrameWnd

  GetMessageBar

  返回一个指向框架窗口的状态条的指针

  OnCreateClient

  创建框架的客户窗口

  OnSetPreviewMode

  设置程序的主框架窗口进入或退出打印预览模式

  NegotiateBorderSpace

  协调边框窗口的边框空间的大小(OLE用途)

  CMDIFrameWnd

  CreateClient

  创建CMDIFrameWnd的MDICLIENT窗,被CWnd的消息处理函数OnCreate调用.

  转下页

  续表

 

  GetWindowMenuPopup

  返回窗口的弹出式菜单

  CDialog

  OnInitDialog

  对话框窗口的初始化

  OnSetFont

  设置对话框控制的文本字体

  OnOK

  模式对话框的OK按钮按下后进行的处理

  OnCancel

  模式对话框的CANCEL按钮按下后进行的处理

  CView

  IsSelected

  测试是否有一个文档被选择(OLE支持)

  OnActivateView

  视窗口激活时调用

  OnActivateFrame

  当包含视窗口的框架窗口变成活动或非活动窗口时调用

  OnBeginPrinting

  打印工作开始时调用,用来分配GDI资源

  OnDraw

  用来屏幕显示、打印、打印预览文档内容

  OnEndPrinting

  打印工作结束时调用,释放GDI资源

  OnEndPrintPreview

  退出打印预览模式时调用

  OnPrepareDC

  OnDraw或OnPrint之前调用,用来准备设备描述表

  OnPreparePrinting

  文档打印或者打印预览前调用,可用来初始化打印对话框

  OnPrint

  用来打印或打印预览文档

  OnUpdate

  用来通知一个视的关联文档内容已经变化

  CDocTemplate

  MatchDocType

  确定文档类型和文档模板匹配时的可信程度

  转下页

  续表

 

  CreateNewDocument

  创建一个新的文档

  CreateNewFrame

  创建一个包含文档和视的框架窗口

  InitialUpdateFrame

  初始化框架窗口,必要时使它可见

  SaveAllModified

  保存所有和模板相关的而且修改了的文档

  CloseAllDocuments

  关闭所有和模板相关的文档

  OpenDocumentFile

  打开指定路径的文件

  SetDefaultTitle

  设置文档窗口缺省显示的标题

  CDocument

  CanCloseFrame

  在关闭显示该文档的边框窗口之前调用

  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_NEW

  OnFileNew

  CWinApp

  ID_FILE_OPEN

  OnFileOpen

  CWinApp

  ID_FILE_CLOSE

  OnFileClose

  CDocument

  ID_FILE_SAVE

  OnFileSave

  CDocument

  ID_FILE_SAVE_AS

  OnFileSaveAs

  CDocument

  ID_FILE_SAVE_COPY_AS

  OnFileSaveCopyAs

  COleServerDoc

  ID_FILE_UPDATE

  OnUpdateDocument

  COleServerDoc

  ID_FILE_PAGE_SETUP

  OnFilePrintSetup

  CWinApp

  转下页

  续表

  ID_FILE_PRINT

  OnFilePrint

  CView

  ID_FILE_PRINT_PREVIEW

  OnFilePrintPreview

  CView

  ID_FILE_MRU_FILE1...FILE16

  OnUpdateRecentFileMenu

  CWinApp

  ID_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_NEW

  OnWindowNew

  CMDIFrameWnd

  ID_WINDOW_ARRANGE

  OnMDIWindowCmd

  CMDIFrameWnd

  ID_WINDOW_CASCADE

   

  ID_WINDOW_TILE_HORZ

   

  ID_WINDOW_TILE_VERT

   

  ID_WINDOW_SPLIT

 

  CSplitterWnd

  ID_APP_ABOUT

   

  ID_APP_EXIT

  OnAppExit

  CWinApp

  ID_HELP_INDEX

  OnHelpIndex

  CWinApp

  ID_HELP_USING

  OnHelpUsing

  CWinApp

  ID_CONTEXT_HELP

  OnContextHelp

  CWinApp

  转下页

  续表

  ID_HELP

  OnHelp

  CWinApp

  ID_DEFAULT_HELP

  OnHelpIndex

  CWinApp

  ID_NEXT_PANE

  OnNextPaneCmd

  CSplitterWnd

  ID_PREV_PANE

  OnNextPaneCmd

  CSplitterWnd

  ID_OLE_INSERT_NEW

   

  ID_OLE_EDIT_LINKS

   

  ID_OLE_VERB_FIRST...LAST

   

  ID_VIEW_TOOLBAR

 

  CFrameWnd

  ID_VIEW_STATUS_BAR

 

  CFrameWnd

  ID_INDICATOR_CAPS

  ID_INDICATOR_NUM

  ID_INDICATOR_SCRL

  ID_INDICATOR_KANA

  OnUpdateKeyIndicator

  CFrameWnd

  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的成员变量

 

  访问限制

  变量名称

  类型

  解释

  √

  public

  m_bAutoDelete

  BOOL

  指定线程结束时是否销毁线程对象本身

  √

  public

  m_hThread

  HANDLE

  当前线程的句柄

  √

  public

  m_nThreadID

  UINT

  当前线程的ID

  √

  public

  m_pMainWnd

  CWnd*

  指向应用程序主窗口的指针

  √

  public

  m_pActiveWnd

  CWnd*

  当OLE SERVER就地激活时指向客户程序主窗口的指针

 

  public

  m_msgCur

  MSG

  当前消息(MSG结构)

 

  public

  m_pThreadParams

  LPVOID

  传递给线程开始函数的参数

 

  public

  m_pfnThreadProc

  函数指针1

  线程开始函数,AFX_THREADPROC类型

 

  public

  m_lpfnOleTermOrFreeLib

  函数指针2

  OLE用途,void (AFXAPI * fn)(BOOL,BOOL)

 

  public

  m_pMessageFilter

  指针

  OLE消息过滤,指向COleMessageFilter对象

 

  protected

  m_ptCursorLast

  CPoint

  最新鼠标位置

 

  protected

  m_nMsgLast

  UINT

  消息队列中最新接收到的消息

  表5-7 CWinApp的成员变量

 

  访问限制

  变量名称

  类型

  解释

  √

  public

  m_pszAppName

  LPCTSTR

  应用程序名称

  √

  public

  m_hInstance

  HINSTANCE

  标志应用程序当前实例句柄

  √

  public

  m_hPrevInstance

  HINSTANCE

  32位程序设为空

  √

  public

  m_lpCmdLine

  LPTSTR

  指向应用程序的命令行字符串

  √

  public

  m_nCmdShow

  int

  指定窗口开始的显示方式

  √

  public

  m_bHelpMode

  BOOL

  标识用户是否在上下文帮助模式

  √

  public

  m_pszExeName

  LPCTSTR

  应用程序的模块名

  √

  public

  m_pszHelpFilePath

  LPCTSTR

  应用程序的帮助文件名,缺省时同模块名

  √

  public

  m_pszProfileName

  LPCTSTR

  应用程序的INI文件名,缺省时同应用程序名

  √

  public

  m_pszRegistryKey

  LPCTSTR

  Register入口,如果不指定,使用INI文件。

 

  public

  m_pDocManager;

  CDocManager *

  指向一个文档模板管理器

 

  protected

  m_hDevMode

  HGLOBAL

  打印设备模式

 

  protected

  m_hDevNames

  HGLOBAL

  打印设备名称

 

  protected

  m_dwPromptContext

  DWORD

  被MESSAGE BOX覆盖的帮助上下文

 

  protected

  m_nWaitCursorCount

  int

  等待光标计数

 

  protected

  m_hcurWaitCursorRestore

  HCURSOR

  保存的光标,在等待光标之后恢复

 

  protected

  m_pRecentFileList

  指针

  指向CRecentFileList对象,最近打开的文件列表

 

  public

  m_atomApp

  ATOM

  DDE用途

 

  public

  m_atomSystemTopic

  m_atomApp

  DDE用途

 

  public

  m_nNumPreviewPages

  UINT

  缺省被打印的页面

 

  public

  m_nSafetyPoolSize

  size_t

  理想尺寸

 

  public

  m_lpfnDaoTerm

  函数指针

  DAO初始化设置时使用

  CDocument的成员变量

  表5-8 文档对象的属性。

 

  访问限制

  变量名称

  类型

  解释

 

  protected

  m_strTitle

  CString

  文档标题

 

  protected

  m_strPathName

  CString

  文档路径

 

  protected

  m_pDocTemplate

  CDocTemplate*

  指向文档模板的指针

 

  protected

  m_viewList

  CPtrList

  关联的视窗口列表

 

  protected

  m_bModified

  BOOL

  文档是否有变化、需要存盘

 

  public

  m_bAutoDelete

  BOOL

  关联视都关闭时是否删除文档对象

 

  public

  m_bEmbedded

  BOOL

  文档是否由OLE创建

  文档模板的属性

  表5-9列出了文档模板的成员变量,5-10列出了单文档模板的成员变量,5-11列出了多文档模板的成员变量。单、多文档模板继承了文档模板的成员变量。

  表5-9 文档模板的数据成员

 

  访问限制

  变量名称

  类型

  解释

 

  public

  m_bAutoDelete

  BOOL

   

  public

  m_pAttachedFactory

  CObject *

   

  public

  m_hMenuInPlace

  HMENU

  就地激活时,OLE客户程序的菜单

 

  public

  m_hAccelInPlace

  HACCEL

  就地激活时,OLE客户程序的快捷键

 

  public

  m_hMenuEmbedding

  HMENU

   

  public

  m_hAccelEmbedding

  HACCEL

   

  public

  m_hMenuInPlaceServer

  HMENU

   

  public

  m_hAccelInPlaceServer

  HACCEL

   

  protected

  m_nIDResource

  UINT

  框架、菜单、快捷键等的资源ID

 

  protected

  m_nIDServerResource

  UINT

   

  public

  m_nIDEmbeddingResource

  UINT

   

  public

  m_nIDContainerResource

  UINT

   

  public

  m_pDocClass

  CRuntimeClass*

  指向文档类的动态创建信息

 

  public

  m_pFrameClass

  CRuntimeClass*

  指向框架类的动态创建信息

 

  public

  m_pViewClass

  CRuntimeClass*

  指向视类的动态创建信息,由字符串m_nIDResource描述

 

  public

  m_pOleFrameClass

  CRuntimeClass*

  指向OLD框架类的动态创建信息

 

  public

  m_pOleViewClass

  CRuntimeClass*

   

  public

  m_strDocStrings

  CString

  描述该文档类型的字符串

  表5-10 单文档模板的成员变量

 

  访问限制

  变量名称

  类型

  解释

 

  protected

  m_pOnlyDoc

  CDocment*

  指向唯一的文档对象

  表5-11 单文档模板的成员变量

 

  访问限制

  变量名称

  类型

  解释

 

  public

  m_hMenuShared

  HMENU

  该模板的MDI子窗口的菜单

 

  public

  m_hAccelTable

  HACCEL

  该模板的MDI子窗口的快捷键

 

  protected

  m_docList

  CPtrList

  该模板的文档列表

 

  protected

  m_nUntitledCount

  UINT

  用来生成文件名的数字,如”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完成,其原型为BOOL OnIdle(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* pDocTemplate;

  pDocTemplate = new CSingleDocTemplate(

  IDR_MAINFRAME,

  RUNTIME_CLASS(CTDoc),

  RUNTIME_CLASS(CMainFrame), // main SDI frame window

  RUNTIME_CLASS(CTView));

  AddDocTemplate(pDocTemplate);

  //第二部分,动态创建文档、视、边框窗口等MFC对象和对应的Windows对象

  //Parse command line for standard shell commands, DDE, file open

  CCommandLineInfo cmdInfo;

  ParseCommandLine(cmdInfo);

  // Dispatch commands specified on the command line

  if (!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。其中,字符串资源描述了文档类型,由七个被“ ”分隔的子字符串组成,各个子串可以通过CDocTemplate的成员函数GetDocString(CString& rString, enum DocStringIndex index)来获取。DocStringIndex是CDocTemplate类定义的枚举变量以区分七个子串,描述如下(英文是枚举变量名称)。

  WindowTitle 应用程序窗口的标题。仅仅对SDI程序指定。

  DocName 用来构造缺省文档名的字符串。当用File菜单的菜单项new创建新文档时,缺省文档名由该字符串加一个数字构成。如果空,使用“unitled”。

  FileNewName 文档类型的名称,在打开File New对话框时显示。

  FilterName 匹配过滤字符串,在File Open对话框用来过滤要显示的文件。如果不指定,File Open对话框的文件类型(file style)不可访问。

  FilterExt 该类型文档的扩展名。如果不指定,则不可访问对话框的文件类型(File Style)。

  RegFileTypeId 文档类型在Windows 注册库中的存储标识。

  RegFileTypeName 文档类型在Windows 注册库中的类型名称。

  文档模板被应用程序对象创建和管理。应用程序类CWinApp有一个CDocManager类型的成员变量m_pDocManager,通过该变量来管理应用程序的文档模板列表,把一些相关的操作委派给CDocManager对象处理。

  CDocManager使用CPtrList类型的m_templateList变量来存储文档模板,并提供了操作文档模板列表的系列函数。

  从语句pDocTemplate = new CSingleDocTemplate(…)可以看出应用程序对象创建模板时传递一个资源ID和三个类的动态创建信息给它:

  IDR_MAINFRAME,资源ID

  RUNTIME_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_bShowSplash;

  BOOL m_bRunEmbedded;

  BOOL m_bRunAutomated;

  enum { FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE,

  AppUnregister, FileNothing = -1 } m_nShellCommand;

  // not valid for FileNew

  CString m_strFileName;

  // valid only for FilePrintTo

  CString m_strPrinterName;

  CString m_strDriverName;

  CString m_strPortName;

  };

  由上述定义可以看出,分析结果分几类:是否OLE激活;应该执行什么动作(FileNew、FileOpen等);传递的参数(打开或打印的文件名,打印设备、端口等)。

  当命令行空时,执行FileNew命令。原因在于CCommandLineInfo的缺省构造函数:

  CCommandLineInfo::CCommandLineInfo()

  {

  m_bShowSplash = TRUE;

  m_bRunEmbedded = FALSE;

  m_bRunAutomated = FALSE;

  m_nShellCommand = FileNew;//指定了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(LPCSTR name),其流程如图5-7所示。

  应用程序对象把打开文件操作委托给文档模板管理器,后者又委托给文档模板对象来执行。如果是SDI程序,则委托给单文档对象;如果是MDI程序,则委托给多文档对象──这是由指针所指对象的实际类型决定的,因为该函数是一个虚拟函数。

  文档模板的OpenDocumentFile

  不论是FileNew还是FileOpen,最后的操作都归结到由文档模板来打开文件(文件名空则创建文件)。

  CSingleDocTemplate::OpenDocumentFile(lpcstr name,BOOL visible)的流程见图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对象被自动销毁,文件句柄被关闭。

原创粉丝点击