⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 13.3.2 mfc框架对serialize函数的调用过程.txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
字号:
13.3.2 MFC框架对Serialize函数的调用过程
另外,在Graphic程序运行后,当单击【文件_保存】和【文件_打开】菜单项时都会出现一个对话
框。但是在CGraphicDoc类中并没有看到相应的菜单命令响应函数,因此我们可以推断这些命令响应
函数一定也是在MFC框架内部实现的。我们可以跟踪【文件_打开】命令的消息响应函数,同时查看
MFC框架对CGraphicDoc类的Serialize函数的调用过程。
同样,在Microsoft提供的MFC源文件: APPDLG.CPP中,可以看到OnFileOpen也是CWinApp的一个成员
函数。井且按照上面查看CWinApp类的OnFileNew函数定义的方法一样,可以发现这个OnFileOpen函
数的声明前面有 afx_msg标识符,说明这个函数确实是菜单命令消息响应函数。该函数的实现代码
如例 13-13所示。 
例 13-13 

void CWinApp: :OnFileOpen() 
{ 
ASSERT(m_pDocManager != NULL); 
m_pDocManager->OnFileOpen(); 
可以看到, CWinApp类的 OnFileOpen函数接着调用了文档管理器的 OnFileOpen函数,该函数的定
义位于 MFC源文件: DOCMGR. CPP文件中,定义代码如例 13-14所示。 
例3-14 
void CDocManager: :OnFileOpen() . 
// prompt the user (with all document templates) 
CString newName ; 
if (!DoPromptFileName(newName, AFX_IDS_OPENFILE, OFN_HIDEREADONLY OFN_FILEMUSTEXIST, TRUE , NULL)) 
return ; 
// open cancelled 
AfxGetApp()->OpenDocumentFile(newName) ; 
// if returns NULL , the user has already been 
alerted 
可以看到,在 CDocManager类的 OnFileOpen函数中调用了一个函数: DoPrompt FileName,从其函
数名大概可以猜到这是一个显示"文件打开/保存"对话框的函数(读者先在此函数调用处设置一个断
点)。该函数的实现代码如例 13-15所示。
例 13-15 

BOOL CDocManager : : DoPromptFileName (CString& fileName , UINT nIDSTitle, DWORD lFlags , 
BOOL bOpenFileDialog, CDocTemplate* pTemplate) { CFileDialog dlgFile(bOpenFileDialog); 
CString title; 
VERIFY(title . LoadString(nIDSTitle)) ; 

dlgFile.m_ofn.Flags 1= lFlags ; 
CString strFilter; 
CString strDefault; 
if (pTemplate != NULL) 
{ 
ASSERT_ VALID(pTemplate) ; 
_AfxAppendFilterSuffix(strFilter, dlgFile . m一ofn, pTemplate , &str Default) ; 
else 
11 do for all doc template 
POSITION pos = m_templateList.GetHeadPosition(); 
BOOL bFirst = TRUE ; 
while (pos != NULL) 
{ CDocTemplate* pTemplate = (CDocTemplate*) m_templateList. GetNext 
500 I如...... 


(pos) ; _Af xAppendFilterSuffix(strF工 lter, dlgFile.m_ofn, pTemplate, bFirst ? 
&strDefault : NULL); bFirst = FALSE; 
. 
1/ append the "*.*" all files filter 
CString allFilter; 

VER工 FY(allFilter.LoadString(AFX_IDS_ALLFILTER)); 
strFilter += al工 Filter; 
strFilter += (TCHAR)'\0'; // next string please 
strFilter += _T("*.*"); 
strFilter += (TCHAR) , \0' ; / / last str工ng 
dlgFile.m_ofn.nMaxCustFilter++; 

dlgFile.m一ofn.lpstrFilter = strFilter; dlgFile.m_ofn.lpstrTitle = title; 
dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH); 
int nResult = dlgFile.DoModal(); 
fileName.ReleaseBuffer(); 
return nResult工 DOK;

可以看到,在 CDocManager类的 DoPromptFileName函数中构造了一个 CFileDialog对象: dlgFile.
由此我们可以知道原来 MFC框架也是利用 CFileDialog这个类来显示"文件打开"对话框的。
让我们回到上述如例 13-14所示 CDocManager类的 OnFileOpen函数,可以看到,接下来,该函数调
用 AfxGetApp函数获得应用程序 (CWinApp)类对象指针,并利用此指针调用 CWinApp对象的
OpenDocumentFile函数。我们在此行代码处也设置一个断点。而 CWinApp类的 OpenDocumentFile
定义位于 AppUl.cpp文件中,代码如例 13-16所示。
例 13-16 

CDocument* CWinApp: :OpenDocumentFile(LPCTSTR lpszFileName) 
{ 
ASSERT(m-pDocManager != NULL); 
return m-pDocManager->OpenDocumentFile(lpszFileName); 
可以看到. CWinApp类的 OpenDocumentFile函数实际上就是去调用 CDocManager类的 
OpenDocumentFile函数。后者位于 DOCMGR.CPP文件中,代码如例 13-17所示。 

CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName) { 
11 find the highest confidence 
POSITION pos = m_templateList . GetHeadPos工 tion() ; 
CDocTemplate ::Confidence bestMatch = CDocTemplate : : noAttempt ; 
CDocTemplate* pBestTemplate = NULL ; 

( CDocument* pOpenDocument =阳LL ; 
TCHAR szPath[_MAX_ PATH] ; 
ASSERT(lstrlen(lpszFileName) < _countof(szPath)) ; 
TCHAR szTernp[_MAX_ PATH] ; 
if (lpszFileNarne[O] , \ 11 

I ) 
++lpszFileName ; 
lstrcpyn(szTemp , lpszFileNarne, _MAX_PATH) ; 
LPTSTR lpszLast = _tcsrchr(szTemp , \ " , ) ; 
if (lpszLast ! = NULL) 

*lpszLast = 0 ; AfxFullPath (szPath , szTemp) ; TCHAR szLinkName[_MAX_PATH] ; if 
(AfxResolveShortcut(AfxGetMainWnd() , szPath, szL工nkNarne, MAX 
PATH) ) lstrcpy(szPath, szLinkName) ; 
while (pos ! = NULL) 
CDocTemplate* pTemplate = (CDocTemplate*)m_ternplateList .GetNext (pos) ; 
ASSERT_KINDOF(CDocTernplate , pTemplate) ; 
CDocTemplate : : Confidence match; 
ASSERT( p OpenDocument NULL) ; 
( rnatch = pTernpl ate->MatchDocType (szPath, pOpenDocument); 
if (match > bestMatch) 

bestMatch = match; 
pBestTernplate = pTernplate ; 

} 
if (match CDocTemplate : : yesAlreadyOpen) break; /1 stop here 
if (pOpenDocument != NULL) 
{ POSITION pos = pOpenDocument->GetFirstViewPosition() ; if (pos ! = NULL) 
{ 
CView* pView =pOpenDocument->GetNextView(pos) ; 11 get first one 
ASSERT_ VALID(pView) ; 
CFrarneWnd* pFrarne = pView->GetParentFrarne() ; 
if (pFrarne != NULL) 
pFrame->ActivateFrame() ; else TRACEO ("Error: Can not find a frame for document to 
activate. 
\n" ) ; 
CFrameWnd* pAppFrame; 
if (pFrame != (pAppFrame = (CFrameWnd*)AfxGetApp()-> m-pMainWnd)) 
{ 

ASSERT_ KINDOF(CFrameWnd, pAppFrame); pAppFrame->ActivateFrame() ; 
} 
else 

TRACEO ( "Error : Can not f工 nda飞riew for document to acti vate . \n ;
u ) 
( return pOpenDocument; 
if (pBestTemplate NULL) 
{ 

AfxMessageBox(AFX_IDP_FA ILED_T。一 OPEN_DOC) ; return NULL; 
@D return pBestTemplate->OpenDocumentFile(szPath); 
读者先在 CDocManager类的 OpenDocumentFile函数开始处设置一个断点。然后阅读此函数的实现代
码,可以看到,在此函数中定义了一个 CDocument类型的指针变量: pOpenDocument (.位置处的代
码),并在后面有一个函数调用: MatchDocType (.位置处的代码),该函数将 pOpenDocument指针变
量作为参数传递进去了。我们在此行代码设置一个断点。在该代码的最后利用文档模板的指针调用 
OpenDocumentFile (.位置处的代码)。因为本程序是一个单文档类型的程序,所以这里实际上调用
的是单文档模板类 ( CSingleDocTemplate )的 OpenDocumentFile函数。该函数的代码可参见本章
前面的内容,可以看到在该函数中,对 IpszPathName变量进行判断的 else子句块下面调用了文档
对象的 OnOpenDocument函数(上面 CSingleDocTemplate类 OpenDocumentFile函数的 .位置处的代
码)。这个函数的定义位于 DocCore.cpp文件中,代码如例 13-18所示。
例 13-18 

BOOL CDocument: :OnOpenDocument(LPCTSTR lpszPathName) 

if (IsModified()) TRACEO ( "Warning : OnOpenDocument replaces an unsaved document . \ n 
11 ) i 
CFileException fe ; ( CFile* pFile = GetFile(lpszPathName , 

" ‘ I 503 
第13 
CFile::rnodeReadICFile::shareDenyWrite, &fe); 
if (pFile NULL) 
{ 

ReportSaveLoadExcept工O且(lpszPathNarne, &fe , FALSE , AFX_IDP_FAILED_TO一OPEN_DOC) ; 
return FALSE; 
DeleteContents() ; 
SetModifiedFlag(); // dirty during de-serialize 

( CArchive loadArchive(pFile , CArchive ::load 	CArchive::bNoFlushOn 
Delete) ; loadArchive.rn-pDocurnent = this; loadArchive . rn_bForceFlat = FALSE; TRY { 
CWaitCursor wait; 
if (pFile->GetLength() != 0) 

③ 	Serialize(loadArchive); // load rne 
loadArchive . Close(); 
ReleaseFile(pFile , FALSE); 

} 
CATCH_ALL(e) 
{ 

ReleaseF工le (pFile , TRUE); 
DeleteContents() ; // rernove fa工led contents 

TRY 

ReportSaveLoadException(lpszPathNarne , e , 
FALSE , AFX_IDP_FAILED_TO_OPEN_DOC); } END TRY DELETE_EXCEPTION(e) ; return FALSE; 
END CATCH ALL 
SetModifiedFlag(FALSE); 	// start off with unrnod工fied
return TRUE; 
这时己经进入到文档基类 (CDocument)中了。在CDocument类的OnOpenDocument函数中,根据得到的
文件名,构造一个 CFile对象 ce位置处的代码),然后利用此对象指针构造一个CArchive对象c(.
位置处的代码),随后有一个Serialize函数的调用 ce位
置处的代码〉。可以在此调用处设置一个断点。
调试运行 Graphic程序,当程序界面出现后,选择【文件飞打开】菜单项,程序进入 CWinApp类的
OnFileOpen函数,该函数将调用CDocManager类的OnFi1eOpen函数。继续运行,程序进入CDocManager
类的DoPromptFileName函数,该函数的作用就是弹出一个文件打开(或保存)对话框。继续运行,程
序就会弹出一个文件打开对话框,从中选择一个文件,例如 Graphic.txt文件并打开。然后程序调
用 CWinApp类的 OpenDocmentFile函数。继续运行,程序回到CDocManager类的OpenDocmentFile函
数。继续运行,程序进入 CSingleD∞Template类的OpenDocumentFile函数。在此函数中,调用文档
对象的 OnOpenDocument函数。因此,程序进入到CDocument类的OnOpenDocument函数,在该函数中,
首先构造CFile对象,接着构造CArchive对象,之后调用Serialize函数,这是一个虚函数,根据多
态性原则,它调用就是子类: CGraphicDoc类的Serialize函数。继续运行,就可以看到程序进入了
CGraphicDoc类的Serialize函数。因为此时是从文件加载数据,所以进入该函数中的else分支。继
续运行程序,即可从弹出的消息框中看到读取的数据。上述过程如图 13.14所示。

文件打开菜单命令 
图 13.14 文件打开菜单命令响应过程 

刚才我们看到一个现象,就是当保存数据后,在不关闭程序窗口的情况下,再次打开同一个文件时,
程序并不会进入Serialize函数中。读者可以再次调试运行Graphic程序,
先单击【文件_保存】菜单项,并选择一个文件,例如Graphic.txt文件保存数据。然后单击【文件
_打开】菜单项,这时程序会依次进入CWinApp类的OnFileOpen函数、 CDocManager类的OnFileOpen
函数,通过DoPrompFileName函数调用显示文件打开对话框。选择我们刚才所保存的文件: 
Graphic.txt,程序进入到 CWinApp类的 OpenDocumentFile函数,然后是CDocManager类的
OpenDocumentFile函数。读者要注意了,这里非常关键!在此函数中,我们单步运行程序,一直运行
到MatchDocType函数调用处。当执行完这个函数调用后,可以发现pOpenDocument这个指针有值了,
也就是说,程序发现这个文件己经与先前的一个文档对象相关联了,即获得了先前的文档对象指针: 
CGraphicDoc类型的指针(如图 13.15所示)。因为此时 pOpenDocument指针不为空了,那么 
CDocManager类的 OpenDocumentFile函数将直接返回 pOpenDocument这个文档类指针(如例 13-17
所示 CDocManager类 OpenDocumentFile函数实现代码中@位置处的代码).这个函数的执行就结束
了,它并没有调用 CSingleDocTemplate类的 OpenDocumentFile函数,也就没有调用CDocument函数
中的Serialize函数,当然其子类: CGraphicDoc中的Serialize函数也就没有被调用。这就是上面在
保存数据之后再次打开同一个文件时,看不到读取的数据消息框的原因。 
图 13.15 pOpenDocument指针已经指向CGraphicDoc类的对象

读者可以试验一下,如果在保存数据之后,再打开另一个文件,这时程序就会弹出消息框,显示读
取到的数据,说明这时调用了CGraphicDoc类的Serialize函数。这就说明文档对象与文件是相关联
的。一旦换了另一个文件,文档对象会将它的数据清空,然后将新文件中的数据与这个对象相关联。
注意在这个过程中,文档对象是同一个文档对象。因为对单文档类型的程序来说,每次只能写一份
文档,因此从效率上考虑,没有必要构造多个文档对象。但是对多文档程序来说,每打开一个文件
都会构造一个新的文档对象。一定要注意,对单文档来说,文档对象本身并不会销毁,它只是将数
据清空,然后再与一个新的
文件相关联。
上面的调试过程比较复杂,读者只要记住.(文件保存和【文件打开】莱单项的命令响应画数都是在
CWinApp类中提供的。CWinApp类有一个成员变量:m_pDocManager. 是指向CDocManager对象的指针,
也就是说, CWinApp负责管理文档管理器,而后者有 
: m_templateList. 
里文档模板,而后者又是用来管理文档类、框架类和视类的,始终让这三个对象三位一体,一起为
文档服务。所以在上述调试过程中,我们可以看到都是由 CWinApp对象转到 CDocManager对象,再
转到CSingleDocTemplate对象,如果牵扯到CDocument对象,就转到文档对象的函数中。读者只要掌
握了这一线索,不管函数调用如何跳来跳去,都会很清楚地知道它们之间的调用逻辑。 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -