MFC File相关命令流程分析

一个APP可以有多个文档模板,一个文档模板可以有多个文档(Document),一个Document可以有多个View。在程序。要在程序中添加新的文档模板可以如下所示:

CSingleDocTemplate*pDocTemplate;
         pDocTemplate = newCSingleDocTemplate(
                   IDR_MAINFRAME,
                   RUNTIME_CLASS(CmfcArchiveDoc),
                   RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
                   RUNTIME_CLASS(CmfcArchiveView));
         if(!pDocTemplate)
                   returnFALSE;
         AddDocTemplate(pDocTemplate);

         CSingleDocTemplate* pDocNew;
         pDocNew = newCSingleDocTemplate(
                   IDR_MAINFRAME,
                   RUNTIME_CLASS(CmfcArchiveDoc),
                   RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
                   RUNTIME_CLASS(CmfcArchiveView));
         if (pDocNew == NULL)
                   returnFALSE;
         AddDocTemplate(pDocNew);

1 程序启动

在程序初始化或者点击菜单新建的时候都会调用OnNewDocument函数。具体流程如下所示:

<1>CWinApp::OnFileNew()//appdlg.cpp

void CWinApp::OnFileNew()
{
         if(m_pDocManager != NULL)
                   m_pDocManager->OnFileNew();
}

<2>CDocManager::OnFileNew()       //docmgr.cpp

void CDocManager::OnFileNew()
{
         if(m_templateList.IsEmpty())
         {
                   TRACE(traceAppMsg, 0, "Error: no document templates registered withCWinApp.\n");
                   AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
                   return;
         }

         CDocTemplate* pTemplate =(CDocTemplate*)m_templateList.GetHead();
         if(m_templateList.GetCount() > 1)
         {
                   //more than one document template to choose from
                   //bring up dialog prompting user
                   CNewTypeDlgdlg(&m_templateList);
                   INT_PTR nID = dlg.DoModal();
                   if(nID == IDOK)
                            pTemplate =dlg.m_pSelectedTemplate;
                   else
                            return;     // none - cancel operation
         }

         ASSERT(pTemplate != NULL);
         ASSERT_KINDOF(CDocTemplate, pTemplate);

         pTemplate->OpenDocumentFile(NULL);
                   //if returns NULL, the user has already been alerted
}

从这个函数可以看到首次运行程序的时候吧,程序会从成员变量m_templateList这里获取程序的模板中的列表,并且弹出对话框供user选择使用哪一个模板

最后在pTemplate->OpenDocumentFile(NULL)调用OpenDocumentFlle函数,因为这个文档模板是单文档的 ,所以pTemplate是CSingleTemplate类型的。

<3> CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bMakeVisible)

CDocument*CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)
{
         returnOpenDocumentFile(lpszPathName, TRUE, bMakeVisible);
}

<4> CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)

这个函数时主要的函数

CDocument*CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOLbMakeVisible)
{
         CDocument* pDocument = NULL;
         CFrameWnd* pFrame = NULL;
         BOOL bCreated = FALSE;      // => docand frame created
         BOOL bWasModified = FALSE;

         if(m_pOnlyDoc != NULL)
         {
                   //already have a document - reinit it
                   pDocument = m_pOnlyDoc;
                   if(!pDocument->SaveModified())
                   {
                            // set a flag to indicate that the document being openedshould not
                            // be removed from the MRU list, if it was being openedfrom there
                            g_bRemoveFromMRU =FALSE;
                            return NULL;       // leave the original one
                   }

                   pFrame =(CFrameWnd*)AfxGetMainWnd();
                   ASSERT(pFrame != NULL);
                   ASSERT_KINDOF(CFrameWnd,pFrame);
                   ASSERT_VALID(pFrame);
         }
         else
         {
                   //create a new document
                   pDocument = CreateNewDocument();
                   ASSERT(pFrame == NULL);     // will becreated below
                   bCreated = TRUE;
         }

         if(pDocument == NULL)
         {
                   AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
                   returnNULL;
         }
         ASSERT(pDocument == m_pOnlyDoc);

         if(pFrame == NULL)
         {
                   ASSERT(bCreated);

                   //create frame - set as main document frame
                   BOOL bAutoDelete =pDocument->m_bAutoDelete;
                   pDocument->m_bAutoDelete =FALSE;
                                               // don't destroy if something goes wrong
                   pFrame =CreateNewFrame(pDocument, NULL);
                   pDocument->m_bAutoDelete =bAutoDelete;
                   if(pFrame == NULL)
                   {
                            AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
                            delete pDocument;       // explicitdelete on error
                            return NULL;
                   }
         }

         if(lpszPathName == NULL)
         {
                   //create a new document
                   SetDefaultTitle(pDocument);

                   //avoid creating temporary compound file when starting up invisible
                   if(!bMakeVisible)
                            pDocument->m_bEmbedded= TRUE;

                   if (!pDocument->OnNewDocument())
                   {
                            // user has been alerted to what failed in OnNewDocument
                            TRACE(traceAppMsg,0, "CDocument::OnNewDocument returnedFALSE.\n");
                            if (bCreated)
                                     pFrame->DestroyWindow();    // will destroydocument
                            return NULL;
                   }
         }
         else
         {
                   CWaitCursor wait;

                   //open an existing document
                   bWasModified =pDocument->IsModified();
                   pDocument->SetModifiedFlag(FALSE);  // not dirty foropen

                   if (!pDocument->OnOpenDocument(lpszPathName))
                   {
                            // user has been alerted to what failed in OnOpenDocument
                            TRACE(traceAppMsg,0, "CDocument::OnOpenDocument returnedFALSE.\n");
                            if (bCreated)
                            {
                                     pFrame->DestroyWindow();    // will destroydocument
                            }
                            else if(!pDocument->IsModified())
                            {
                                     // original document is untouched
                                     pDocument->SetModifiedFlag(bWasModified);
                            }
                            else
                            {
                                     // we corrupted the original document
                                     SetDefaultTitle(pDocument);

                                     if (!pDocument->OnNewDocument())
                                     {
                                               TRACE(traceAppMsg,0, "Error: OnNewDocument failed after trying"
                                                        "to open a document - trying to continue.\n");
                                               // assume we can continue
                                     }
                            }
                            return NULL;       // open failed
                   }
                   pDocument->SetPathName(lpszPathName,bAddToMRU);
                   pDocument->OnDocumentEvent(CDocument::onAfterOpenDocument);
         }

         CWinThread* pThread = AfxGetThread();
         ASSERT(pThread);
         if(bCreated && pThread->m_pMainWnd == NULL)
         {
                   //set as main frame (InitialUpdateFrame will show the window)
                   pThread->m_pMainWnd =pFrame;
         }
         InitialUpdateFrame(pFrame, pDocument,bMakeVisible);

         returnpDocument;
}

因为是第一次打开程序,所以会新建一个CDocument,以及MainFrame,最后就会调用我们自己写的OnNewDocument。

<5> CmfcArchiveDoc::OnNewDocument()

BOOLCmfcArchiveDoc::OnNewDocument()
{
         if(!CDocument::OnNewDocument())
                   returnFALSE;

         // TODO: 在此添加重新初始化代码
         // (SDI 文档将重用该文档)

         returnTRUE;
}

在这个函数里面会调用CDocument::OnNewDocument

总结:

<1>CWinApp::OnFileNew()//appdlg.cpp

<2>CDocManager::OnFileNew()       //docmgr.cpp

<3>CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bMakeVisible)

<4>CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)

<5>CmfcArchiveDoc::OnNewDocument()

2 文件新建

文件新建的流程跟程序初次打开流程一样,只不过在CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)

中函数执行的内容可能不同,如果打开的是同一个文档模板的话,那么就不会再去创建文档,FrameWindow。否则的话就要新建新的模板。

3 文件打开

文件打开以及文件关闭会执行的命令是ID_FILE_OPEN,ID_FILE_SAVE。这两个命令都是跟文档串行化有关的,一个是存档,一个载入。

<1> CWinApp::OnFileOpen()

void CWinApp::OnFileOpen()
{
         ENSURE(m_pDocManager != NULL);
         m_pDocManager->OnFileOpen();
}

<2> CDocManager::OnFileOpen()

void CDocManager::OnFileOpen()
{
         // prompt theuser (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
}

其中DoPromptFileName函数会弹出一个对话框提示选择要打开的文件,并且在选定后会保存文件的名字和路径。

<3> CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)

CDocument*CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
         ENSURE_VALID(m_pDocManager);
         returnm_pDocManager->OpenDocumentFile(lpszFileName);
}

<4> CDocManager::OpenDocumentFile(LPCTSTRlpszFileName)

CDocument*CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)
{
         returnOpenDocumentFile(lpszFileName, TRUE);
}

<5>CDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU)

CDocument*CDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU)
{
         if(lpszFileName == NULL)
         {
                   AfxThrowInvalidArgException();
         }
         // find thehighest confidence
         POSITION pos =m_templateList.GetHeadPosition();
         CDocTemplate::Confidence bestMatch =CDocTemplate::noAttempt;
         CDocTemplate* pBestTemplate = NULL;
         CDocument* pOpenDocument = NULL;

         TCHAR szPath[_MAX_PATH];
         ASSERT(lstrlen(lpszFileName) <_countof(szPath));
         TCHAR szTemp[_MAX_PATH];
         if(lpszFileName[0] == '\"')
                   ++lpszFileName;
         Checked::tcsncpy_s(szTemp,_countof(szTemp), lpszFileName, _TRUNCATE);
         LPTSTR lpszLast = _tcsrchr(szTemp, '\"');
         if(lpszLast != NULL)
                   *lpszLast = 0;

         if(AfxFullPath(szPath, szTemp) == FALSE )
         {
                   ASSERT(FALSE);
                   returnNULL; // We won't open the file. MFC requires pathswith
                                //length < _MAX_PATH
         }

         TCHAR szLinkName[_MAX_PATH];
         if(AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH))
                   Checked::tcscpy_s(szPath,_countof(szPath), szLinkName);

         while(pos != NULL)
         {
                   CDocTemplate* pTemplate =(CDocTemplate*)m_templateList.GetNext(pos);
                   ASSERT_KINDOF(CDocTemplate,pTemplate);

                   CDocTemplate::Confidencematch;
                   ASSERT(pOpenDocument ==NULL);
                   match =pTemplate->MatchDocType(szPath, pOpenDocument);
                   if(match > bestMatch)
                   {
                            bestMatch = match;
                            pBestTemplate =pTemplate;
                   }
                   if(match == CDocTemplate::yesAlreadyOpen)
                            break;      // stop here
         }

         if (pOpenDocument != NULL)
         {
                   POSITION posOpenDoc =pOpenDocument->GetFirstViewPosition();
                   if(posOpenDoc != NULL)
                   {
                            CView* pView =pOpenDocument->GetNextView(posOpenDoc); // getfirst one
                            ASSERT_VALID(pView);
                            CFrameWnd* pFrame =pView->GetParentFrame();

                            if (pFrame == NULL)
                                     TRACE(traceAppMsg,0, "Error: Can not find a frame for documentto activate.\n");
                            else
                            {
                                     pFrame->ActivateFrame();

                                     if (pFrame->GetParent() != NULL)
                                     {
                                               CFrameWnd*pAppFrame;
                                               if (pFrame != (pAppFrame =(CFrameWnd*)AfxGetApp()->m_pMainWnd))
                                               {
                                                        ASSERT_KINDOF(CFrameWnd,pAppFrame);
                                                        pAppFrame->ActivateFrame();
                                               }
                                     }
                            }
                   }
                   else
                            TRACE(traceAppMsg,0, "Error: Can not find a view for document toactivate.\n");

                   returnpOpenDocument;
         }

         if(pBestTemplate == NULL)
         {
                   AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);
                   returnNULL;
         }

         return pBestTemplate->OpenDocumentFile(szPath,bAddToMRU, TRUE);
}

程序会判断pDocument是否是NULL,是的话就会调用pBestTemplate->OpenDocumentFile(szPath,bAddToMRU, TRUE);

第一次打开一个文件的时候,必然会调用这个函数的。

<6> CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)

这个函数和程序启动以及新建操作的函数一样,关键是调用的部分不用。它调用的部分是:

lpszPathName!= NULL 的部分

else
         {
                   CWaitCursor wait;
                   //open an existing document
                   bWasModified =pDocument->IsModified();
                   pDocument->SetModifiedFlag(FALSE);  // not dirty foropen
                   if (!pDocument->OnOpenDocument(lpszPathName))
                   {
                            // user has been alerted to what failed in OnOpenDocument
                            TRACE(traceAppMsg,0, "CDocument::OnOpenDocument returnedFALSE.\n");
                            if (bCreated)
                            {
                                     pFrame->DestroyWindow();    // will destroydocument
                            }
                            else if(!pDocument->IsModified())
                            {
                                     // original document is untouched
                                     pDocument->SetModifiedFlag(bWasModified);
                            }
                            else
                            {
                                     // we corrupted the original document
                                     SetDefaultTitle(pDocument);
                                     if (!pDocument->OnNewDocument())
                                     {
                                               TRACE(traceAppMsg,0, "Error: OnNewDocument failed after trying"
                                                        "to open a document - trying to continue.\n");
                                               // assume we can continue
                                     }
                            }
                            return NULL;       // open failed
                   }

<7> CDocument::OnOpenDocument(LPCTSTR lpszPathName)

Serialize(loadArchive);     // load me

OnOpenDocument函数中会调用Serialize()函数。这个Serialize函数是个虚函数,从而会调用的是CmfcArchiveDoc::Serialize(CArchive& ar)

总结:

<1> CWinApp::OnFileOpen()

<2> CDocManager::OnFileOpen()

<3> CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)

<4> CDocManager::OpenDocumentFile(LPCTSTRlpszFileName)

<5>CDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToMRU)

<6> CSingleDocTemplate::OpenDocumentFile(LPCTSTRlpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)

<7> CDocument::OnOpenDocument(LPCTSTR lpszPathName)

其中要是注意的是如果已经打开了一个文档并且没有关闭,那么在再次打开的时候不会调用序列化函数,原因是当已经打开了一个文档,再次打开它的时候,CDocManager::OnFileOpen()这个函数中的pDocument已经有值了,所以不会再去打开了。

4  文件保存

<1> void CDocument::OnFileSave()         //doccore.cpp

<2> BOOL CDocument::DoFileSave()     //doccore.cpp

<3> BOOL CDocument::DoSave(LPCTSTR lpszPathName,BOOL bReplace)         //doccore.cpp

<4> BOOL CDocument::OnSaveDocument(LPCTSTRlpszPathName)            //doccore.cpp

<5> void CmfcArchiveDoc::Serialize(CArchive& ar)

这个就是文件的保存流程。

MFC File相关命令流程分析

时间: 2024-10-10 10:25:26

MFC File相关命令流程分析的相关文章

基于linux与busybox的reboot命令流程分析

http://www.xuebuyuan.com/736763.html 基于Linux与Busybox的Reboot命令流程分析 *************************************************************************************************************************** 作者:EasyWave                                                

Memcached 源码分析--命令流程分析

一.执行命令 首先是启动memcached 自带参数如下: <span style="font-size:18px;">-p <num> 设置TCP端口号(默认设置为: 11211) -U <num> UDP监听端口(默认: 11211, 0 时关闭) -l <ip_addr> 绑定地址(默认:所有都允许,无论内外网或者本机更换IP,有安全隐患,若设置为127.0.0.1就只能本机访问) -c <num> max simult

Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析

本篇博客旨在分析Android中广播相关的源码流程. 一.基础知识 广播(Broadcast)是一种Android组件间的通信方式. 从本质上来看,广播信息的载体是intent.在这种通信机制下,发送intent的对象就是广播发送方,接收intent的对象就是广播接收者. 在Android中,为广播接收者定义了一个单独的组件:BroadcastReceiver. 1 BroadcastReceiver的注册类型 在监听广播前,要将BroadcastReceiver注册到系统中. Broadcas

Android 7.0 ActivityManagerService(8) 进程管理相关流程分析(2)

前一篇博客进程管理相关流程分析(1)里, 我们介绍了AMS中updateLruProcessLocked函数相关的流程. updateLruProcessLocked只是按照进程中运行的组件,粗略地定义了不同进程的优先级. 实际上,Android根据进程的oom_adj进行了更加细致的进程分类, 而AMS中的updateOomAdjLocked函数,就是用于更新进程的oom_adj值. 本篇博客中,我们来看看AMS中updateOomAdjLocked相关的流程. 一.ProcessList.j

u-boot启动流程分析(1)_平台相关部分

转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—>cpu”框架,介绍u-boot中平台相关部分的启动流程.并通过对启动流程的简单分析,掌握u-boot移植的基本方法. 注1:本文所使用的u-boot版本,是2016/4/23从u-boot官网(git://git.denx.de/u-boot.git)导入的一个快照,具体可参考“https://github

第2章 rsync算法原理和工作流程分析

本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是rsync系列篇: 1.rsync(一):基本命令和用法 2.rsync(二):inotify+rsync详细说明和sersync 3.rsync算法原理和工作流程分析 4.rsync技术报告(翻译) 5.rsync工作机制(翻译) 6.man rsync翻译(rsync命令中文手册) 本文目录: 1.1 需要解决的问题 1.2 rsync增量传输算法原理 1.3 通过示例分析r

Android bluetooth介绍(四): a2dp connect流程分析

关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_get_properties.AUDIO.DBUS版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! Andr

Android5 Zygote 与 SystemServer 启动流程分析

Android5 Zygote 与 SystemServer 启动流程分析 Android5 Zygote 与 SystemServer 启动流程分析 前言 zygote 进程 解析 zygoterc 启动 SystemServer 执行 ZygoteInitrunSelectLoop SystemServer 启动过程 Zygote 的 fork 本地方法分析 forkSystemServer ZygoteHookspreFork 创建 system_server 进程 ZygoteHooks

Linux系统启动流程分析与关机流程

Linux 系统启动流程分析 Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导. 运行 init. 系统初始化. 建立终端. 用户登录系统. init程序的类型: SysV: init, CentOS 5之前, 配置文件: /etc/inittab. Upstart: init,CentOS 6, 配置文件: /etc/inittab, /etc/init/*.conf. Systemd: systemd, CentOS 7,配置文件: /usr/lib/