程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> MFC教程(5)-- MFC對象的創建(1)

MFC教程(5)-- MFC對象的創建(1)

編輯:關於VC++

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對象被自動銷毀,文件句柄被關閉。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved