浅谈MFC多文档视图中文档是如何对应多个视图的?

MfC打开过程详解及应用                          

  

本文主要介绍:在MFC中,菜单打开命令的响应过程。

一、MFC打开命令的响应过程:

File->Open 对应的ID为ID_FILE_OPEN,其响应过程如下:

注:如果自己已将ID_FLIE_OPEN在MFC中重载了,则会直接响应重载函数,不会按以下过程响应。

1.点击File->Open,首先响应的函数为: CWinApp::OnFileOpen(),其函数原型为:

[cpp] view plain copy print ?
  1. void CWinApp::OnFileOpen()  
  2. {  
  3.     ASSERT(m_pDocManager != NULL);  
  4.         m_pDocManager->OnFileOpen();  
  5. }  
void CWinApp::OnFileOpen()
{
	ASSERT(m_pDocManager != NULL);
        m_pDocManager->OnFileOpen();
}

2.由上面的程序可知,接着调用的是: CDocManager::onFileOpen(),该函数功能是:显示打开文件的对话框,并获取文件的路径,其函数原型为:

[cpp] view plain copy print ?
  1. void CDocManager::OnFileOpen()  
  2. {  
  3.        // prompt the user (with all document templates)  
  4.     CString newName;  //弹出打开文件的对话框,获取文件路径  
  5.     if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))  
  6.         return// open cancelled  
  7.     AfxGetApp()->OpenDocumentFile(newName);  
  8.       // if returns NULL, the user has already been alerted  
  9. }  
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
}


3.接着调用函数: CWinApp::OpenDocumentFile(LPCTSTR lpszFileName),其函数原型为:

[cpp] view plain copy print ?
  1. CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)  
  2. {  
  3.     ASSERT(m_pDocManager != NULL);  
  4.     return m_pDocManager->OpenDocumentFile(lpszFileName);  
  5. }  
CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
    ASSERT(m_pDocManager != NULL);
    return m_pDocManager->OpenDocumentFile(lpszFileName);
}

4.再调用函数: CDocManager::OpenDocumentFile(LPCTSTR lpszFileName),该函数遍历文档模板,对每个文档进行匹配,若该文件已经在某个文档中打开,则会激活该文档视图,否则用匹配的文档模板,调用下一个打开函数,其原型为:

[cpp] view plain copy print ?
  1. CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)  
  2. {  
  3.        // find the highest confidence  
  4.     POSITION pos = m_templateList.GetHeadPosition();  
  5.     CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt;  
  6.     CDocTemplate* pBestTemplate = NULL;  
  7.     CDocument* pOpenDocument = NULL;  
  8.        lstrcpyn(szTemp, lpszFileName, _MAX_PATH);  
  9.        LPTSTR lpszLast = _tcsrchr(szTemp, '"');  
  10.   
  11.        if (lpszLast != NULL)  
  12.               *lpszLast = 0;  
  13.                 
  14.        AfxFullPath(szPath, szTemp);  
  15.        TCHAR szLinkName[_MAX_PATH];  
  16.          
  17.        if (AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH))  
  18.               lstrcpy(szPath, szLinkName);  
  19.   
  20.        while (pos != NULL)  
  21.        {  
  22.               CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetNext(pos);  
  23.               ASSERT_KINDOF(CDocTemplate, pTemplate);  
  24.   
  25.               CDocTemplate::Confidence match;  
  26.               ASSERT(pOpenDocument == NULL);  
  27.   
  28.               match = pTemplate->MatchDocType(szPath, pOpenDocument);  
  29.               if (match > bestMatch)  
  30.               {  
  31.                      bestMatch = match;  
  32.                      pBestTemplate = pTemplate;  
  33.               }  
  34.   
  35.               if (match == CDocTemplate::yesAlreadyOpen)  
  36.                      break;      // stop here  
  37.        }  
  38.        if (pOpenDocument != NULL)  
  39.        {  
  40.               POSITION pos = pOpenDocument->GetFirstViewPosition();  
  41.               if (pos != NULL)  
  42.               {  
  43.                      CView* pView = pOpenDocument->GetNextView(pos); // get first one  
  44.                      ASSERT_VALID(pView);  
  45.                      CFrameWnd* pFrame = pView->GetParentFrame();  
  46.                      if (pFrame != NULL)  
  47.                             pFrame->ActivateFrame();  
  48.                      else  
  49.                             TRACE0("Error: Can not find a frame for document to activate.\n");  
  50.   
  51.                      CFrameWnd* pAppFrame;  
  52.                      if (pFrame != (pAppFrame = (CFrameWnd*)AfxGetApp()->m_pMainWnd))  
  53.                      {  
  54.                             ASSERT_KINDOF(CFrameWnd, pAppFrame);  
  55.                             pAppFrame->ActivateFrame();  
  56.                      }  
  57.               }  
  58.               else  
  59.               {  
  60.                      TRACE0("Error: Can not find a view for document to activate.\n");  
  61.               }  
  62.               return pOpenDocument;  
  63.        }  
  64.        if (pBestTemplate == NULL)  
  65.        {  
  66.               AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);  
  67.               return NULL;  
  68.        }  
  69.        return pBestTemplate->OpenDocumentFile(szPath);  
  70. }  
CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)
{
       // find the highest confidence
    POSITION pos = m_templateList.GetHeadPosition();
    CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt;
    CDocTemplate* pBestTemplate = NULL;
    CDocument* pOpenDocument = NULL;
       lstrcpyn(szTemp, lpszFileName, _MAX_PATH);
       LPTSTR lpszLast = _tcsrchr(szTemp, '"');

       if (lpszLast != NULL)
              *lpszLast = 0;
			  
       AfxFullPath(szPath, szTemp);
       TCHAR szLinkName[_MAX_PATH];
	   
       if (AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH))
              lstrcpy(szPath, szLinkName);

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

              CDocTemplate::Confidence match;
              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 pos = pOpenDocument->GetFirstViewPosition();
              if (pos != NULL)
              {
                     CView* pView = pOpenDocument->GetNextView(pos); // get first one
                     ASSERT_VALID(pView);
                     CFrameWnd* pFrame = pView->GetParentFrame();
                     if (pFrame != NULL)
                            pFrame->ActivateFrame();
                     else
                            TRACE0("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
              {
                     TRACE0("Error: Can not find a view for document to activate.\n");
              }
              return pOpenDocument;
       }
       if (pBestTemplate == NULL)
       {
              AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);
              return NULL;
       }
       return pBestTemplate->OpenDocumentFile(szPath);
}

5.调用函数:CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible),该函数是多文档打开函数,先创建文档的框架窗口,然后判断路径是否为空,如果为空,则重新设置文档路径;最后,调用InitialUpdateFrame显示框架窗口。其原型为:

[cpp] view plain copy print ?
  1. CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)  
  2. {  
  3.        CDocument* pDocument = CreateNewDocument();  
  4.        if (pDocument == NULL)  
  5.        {  
  6.               TRACE0("CDocTemplate::CreateNewDocument returned NULL.\n");  
  7.               AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);  
  8.               return NULL;  
  9.        }  
  10.   
  11.        ASSERT_VALID(pDocument);  
  12.   
  13.        BOOL bAutoDelete = pDocument->m_bAutoDelete;  
  14.        pDocument->m_bAutoDelete = FALSE;   // don't destroy if something goes wrong  
  15.        CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);  
  16.        pDocument->m_bAutoDelete = bAutoDelete;  
  17.        if (pFrame == NULL)  
  18.        {  
  19.               AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);  
  20.               delete pDocument;       // explicit delete on error  
  21.               return NULL;  
  22.        }  
  23.   
  24.        ASSERT_VALID(pFrame);  
  25.   
  26.        if (lpszPathName == NULL)  
  27.        {  
  28.               // create a new document - with default document name  
  29.               SetDefaultTitle(pDocument);  
  30.   
  31.               // avoid creating temporary compound file when starting up invisible  
  32.               if (!bMakeVisible)  
  33.                      pDocument->m_bEmbedded = TRUE;  
  34.   
  35.               if (!pDocument->OnNewDocument())  
  36.               {  
  37.                      // user has be alerted to what failed in OnNewDocument  
  38.                      TRACE0("CDocument::OnNewDocument returned FALSE.\n");  
  39.                      pFrame->DestroyWindow();  
  40.                      return NULL;  
  41.               }  
  42.   
  43.               // it worked, now bump untitled count  
  44.               m_nUntitledCount++;  
  45.        }  
  46.        else  
  47.        {  
  48.               // open an existing document  
  49.               CWaitCursor wait;  
  50.               if (!pDocument->OnOpenDocument(lpszPathName))  
  51.               {  
  52.                      // user has be alerted to what failed in OnOpenDocument  
  53.                      TRACE0("CDocument::OnOpenDocument returned FALSE.\n");  
  54.                      pFrame->DestroyWindow();  
  55.                      return NULL;  
  56.               }  
  57.               pDocument->SetPathName(lpszPathName);  
  58.        }  
  59.   
  60.        InitialUpdateFrame(pFrame, pDocument, bMakeVisible);  
  61.        return pDocument;  
  62. }  
CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)
{
       CDocument* pDocument = CreateNewDocument();
       if (pDocument == NULL)
       {
              TRACE0("CDocTemplate::CreateNewDocument returned NULL.\n");
              AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
              return NULL;
       }

       ASSERT_VALID(pDocument);

       BOOL bAutoDelete = pDocument->m_bAutoDelete;
       pDocument->m_bAutoDelete = FALSE;   // don't destroy if something goes wrong
       CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);
       pDocument->m_bAutoDelete = bAutoDelete;
       if (pFrame == NULL)
       {
              AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
              delete pDocument;       // explicit delete on error
              return NULL;
       }

       ASSERT_VALID(pFrame);

       if (lpszPathName == NULL)
       {
              // create a new document - with default document name
              SetDefaultTitle(pDocument);

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

              if (!pDocument->OnNewDocument())
              {
                     // user has be alerted to what failed in OnNewDocument
                     TRACE0("CDocument::OnNewDocument returned FALSE.\n");
                     pFrame->DestroyWindow();
                     return NULL;
              }

              // it worked, now bump untitled count
              m_nUntitledCount++;
       }
       else
       {
              // open an existing document
              CWaitCursor wait;
              if (!pDocument->OnOpenDocument(lpszPathName))
              {
                     // user has be alerted to what failed in OnOpenDocument
                     TRACE0("CDocument::OnOpenDocument returned FALSE.\n");
                     pFrame->DestroyWindow();
                     return NULL;
              }
              pDocument->SetPathName(lpszPathName);
       }

       InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
       return pDocument;
}

6.最后调用函数:CDocument::OnOpenDocument(LPCTSTR lpszPathName),该函数一般在“***Doc.cpp”中重载(*** 为工程名),因为不同的文件打开的过程不同,所以可以根据需求,在“***Doc.cpp”中改写OnOpenDocument(LPCTSTR lpszPathName)函数,从而打开相应的文件。其默认生成原型为:

[cpp] view plain copy print ?
  1. BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)  
  2. {  
  3.   
  4.        if (IsModified())  
  5.               TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");  
  6.   
  7.        CFileException fe;  
  8.        CFile* pFile = GetFile(lpszPathName,CFile::modeRead|CFile::shareDenyWrite, &fe);  
  9.   
  10.        if (pFile == NULL)  
  11.        {  
  12.               ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);  
  13.               return FALSE;  
  14.        }  
  15.   
  16.        DeleteContents();  
  17.        SetModifiedFlag();  // dirty during de-serialize  
  18.   
  19.        CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);  
  20.        loadArchive.m_pDocument = this;  
  21.        loadArchive.m_bForceFlat = FALSE;  
  22.   
  23.        TRY  
  24.        {  
  25.               CWaitCursor wait;  
  26.               if (pFile->GetLength() != 0)  
  27.                      Serialize(loadArchive);     // load me  
  28.               loadArchive.Close();  
  29.               ReleaseFile(pFile, FALSE);  
  30.        }  
  31.        CATCH_ALL(e)  
  32.        {  
  33.               ReleaseFile(pFile, TRUE);  
  34.               DeleteContents();   // remove failed contents  
  35.    
  36.               TRY  
  37.               {  
  38.                      ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);  
  39.               }  
  40.               END_TRY  
  41.               DELETE_EXCEPTION(e);  
  42.               return FALSE;  
  43.        }  
  44.        END_CATCH_ALL  
  45.        SetModifiedFlag(FALSE);     // start off with unmodified  
  46.        return TRUE;  
  47. }  
BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
{

       if (IsModified())
              TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");

       CFileException fe;
       CFile* pFile = GetFile(lpszPathName,CFile::modeRead|CFile::shareDenyWrite, &fe);

       if (pFile == NULL)
       {
              ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
              return FALSE;
       }

       DeleteContents();
       SetModifiedFlag();  // dirty during de-serialize

       CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
       loadArchive.m_pDocument = this;
       loadArchive.m_bForceFlat = FALSE;

       TRY
       {
              CWaitCursor wait;
              if (pFile->GetLength() != 0)
                     Serialize(loadArchive);     // load me
              loadArchive.Close();
              ReleaseFile(pFile, FALSE);
       }
       CATCH_ALL(e)
       {
              ReleaseFile(pFile, TRUE);
              DeleteContents();   // remove failed contents
 
              TRY
              {
                     ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
              }
              END_TRY
              DELETE_EXCEPTION(e);
              return FALSE;
       }
       END_CATCH_ALL
       SetModifiedFlag(FALSE);     // start off with unmodified
       return TRUE;
}

二、总结

通过上述介绍,MFC打开命令的响应过程如下:

         1.首先调用CWinApp::OnFileOpen(),该函数调用CDocManager::OnFileOpen()函数;

         2.CDocManager::OnFileOpen()显示打开文件的对话框,并获取文件的路径,然后该函数调用CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)函数;

         3.CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)函数直接调用CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)函数;

         4.CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)函数遍历文档模板,对每个文档进行匹配,若该文件已经在某个文档中打开,则会激活该文档视图,否则用匹配的文档模板,调用CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)函数;

         5.CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)函数先创建文档的框架窗口,然后判断路径是否为空,如果为空,则重新设置文档路径;接着,调用InitialUpdateFrame显示框架窗口,最后调用CDocument::OnOpenDocument(LPCTSTR lpszPathName)函数;

         6.CDocument::OnOpenDocument(LPCTSTR lpszPathName)函数一般在“***Doc.cpp”中重载(*** 为工程名),因为不同的文件打开的过程不同,所以可以根据需求,在“***Doc.cpp”中改写OnOpenDocument(LPCTSTR lpszPathName)函数,从而打开相应的文件。

三、运用

由于打开命令的响应过程,是一系列函数依次调用,因此,可以调用其中的一个环节,来打开文件。以打开图片为例,介绍一下应用:

在打开文件时,弹出的对话框默认的文件“所有文件(*.*)”,如下图:

浅谈MFC多文档视图中文档是如何对应多个视图的?_第1张图片

有时候我们需要打开特定的文件类型,如BMP、Jpg、Tif等类型,即需要弹出下面的对话框:

浅谈MFC多文档视图中文档是如何对应多个视图的?_第2张图片

该功能实现的过程如下:(***代表工程名)

1.在“***.cpp”中重载ID_FILE_OPEN的响应函数OnFileOpen() ,即对ID_FILE_OPEN在C***App的类中添加一个响应函数,函数名为OnFileOpen();

注:如果ID_FILE_OPEN已经重载为:ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen),要把这行代码屏蔽,不然点击打开时,仍默认原来的响应,不会响应自己新重载的函数。

2.编写C***App::OnFileOpen()函数:

[cpp] view plain copy print ?
  1. void CMyIMGApp::OnFileOpen()   
  2. {  
  3.     // TODO: Add your command handler code here  
  4.     CString strOpenFilter = "所有文件(*.*)|*.bmp; *.dib; *.gif; *.jpg; *.jpe; *.jpeg; *.tif; *.tiff; *.raw|位图文件 (*.bmp;*.dib)|*.bmp; *.dib|GIF文件 (*.gif)|*.gif|JPEG文件 (*.jpg;*.jpe;*.jpeg)|*.jpg; *.jpe; *.jpeg|TIFF文件 (*.tif;*.tiff)|*.tif; *.tiff|RAW文件(*.raw)|*.raw|";  
  5.     CFileDialog FileDlg(TRUE, "*.bmp", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strOpenFilter);  
  6.     if (FileDlg.DoModal() == IDOK)  
  7.         OpenDocumentFile(FileDlg.m_ofn.lpstrFile);  
  8. }  
void CMyIMGApp::OnFileOpen() 
{
	// TODO: Add your command handler code here
	CString strOpenFilter = "所有文件(*.*)|*.bmp; *.dib; *.gif; *.jpg; *.jpe; *.jpeg; *.tif; *.tiff; *.raw|位图文件 (*.bmp;*.dib)|*.bmp; *.dib|GIF文件 (*.gif)|*.gif|JPEG文件 (*.jpg;*.jpe;*.jpeg)|*.jpg; *.jpe; *.jpeg|TIFF文件 (*.tif;*.tiff)|*.tif; *.tiff|RAW文件(*.raw)|*.raw|";
	CFileDialog FileDlg(TRUE, "*.bmp", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strOpenFilter);
	if (FileDlg.DoModal() == IDOK)
		OpenDocumentFile(FileDlg.m_ofn.lpstrFile);
}

该函数是直接调用了上述环节的CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)函数,从而实现文件的打开。

说明:点击打开菜单时,直接的响应函数是自己重载后的函数,通过重载只是改变了前两个环节,最后通过调用OpenDocumentFile(LPCTSTR lpszFileName)函数,使后面函数依次被调用。

   到此知道了文档是如何被打开的,接着往下看...                   


当打开一个文档时,MFC如何选择所使用的文档模板对象?

当把一个 文档名 作为 变量 来调用CWinApp::OpenDocumentFile()时,MFC是 如何选择 用来 打开一个给定文档的 文档模板对象的呢?

      如果跟踪CWinApp::OpenDocumentFile()函数的整个调用过程,会发现该函数只是通过存储在 应用程序对象 中的m_pDocManager指针简单的调用CDocManager::OpenDocumentFile()函数,后面的这个函数完成选择与文档扩展名最匹配的文档模板对象的全部工作。

      为了完成这一部分的处理,CDocManager::OpenDocumentFile()函数遍历它的文档模板对象表,并为表的每个成员(成员为文档模板对象)调用CDocTemplate::MatchDocType()函数,传递的参数是 要打开的 文档的 文件名。

 

      而CDocTemplate::MatchDocType()执行下面的逻辑:

      (1)遍历当前打开的文档对象的列表,查找它们中的哪一个的文件名与我们的变量相同。如果能找到,返回 指向这个文档对象的指针 和 yesAlreadyOpen确认值。

      (2)如果文件名与所有打开的文档都不匹配,那么检查它的扩展名是否与 文档模板字符串 中的filterExt字串匹配。如果匹配的话,返回一个yesAttemptNative确认值。否则返回yesAttemptForeign确认值。

 

      在遍历中,CDocManager::OpenDocumentFile()函数始终保留一个指向文档模板对象的指针,该对象提供了最后的确认,最后的确认值决定了CDocManager::OpenDocumentFile()函数将要发生的动作。

      @如果文件名与一个已经打开的文档对象匹配(确认值==yesAlreadyOpen),那么激活这个文档的框架窗口和视图。

      @如果文件的扩展名与某个文档模板对象的filterExt字串匹配(确认值==yesAlreadyNative),那么把这个文档模板对象作为最好的候选模板来使用,并调用它的OpenDocumentFile()函数。

      @如果文件名的扩展名与任何文档模板对象的filterExt都不匹配(确认值==yesAlreadyForeign),那么随即的选择第一个注册过的文档模板对象作为最匹配的对象,并调用它的OpenDocumentFile()函数。注意:这种随即的选择通常都是错误的,在这种情况下,文档对象的Serialize()函数将可能触发一个错误异常,CDocument::OnOpenDocument()函数的默认句柄将捕获该异常并显示一条错误信息。

      从上面的分析中可以看出,如果打算把自己的 某个文档模板对象 作为所有 扩展名与特殊filterExt字串  不匹配 的文档的默认模板,那么该 默认的文档模板对象 应该是AddDocTemplate()注册的第一个模板。

     其中核心的CDocManager::OpenDocumentFile()函数的伪代码如下:

[cpp]  view plain copy
  1. CDocument*  CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)  
  2. {  
  3.       //1、遍历应用程序的文档模板列表  
  4.       POSITION pos=m_templateList.GetHeadposition();//文档模板列表中的第一个文档模板对象的位置  
  5.       CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt;  
  6.       CDocTemplate* pBestTemplate = NULL;//为与 文件 最匹配的 文档模板 准备的指针  
  7.       CDocument* pOpenDocument=NULL;//保存已经打开的文档指针,由MatchDocType()函数赋值  
  8.       TCHAR szPath[_MAX_PATH];  
  9.       while(pos != NULL)  
  10.       {  
  11.             CDocTemplate* pTemplate=(CDocTemplate*)m_templateList.GetNext(pos);//每个文档模板对象的指针                   
  12.             //计算这个文档模板对象的确认值  
  13.             CDocTemplate::Confidence match;  
  14.             match=pTemplate->MatchDocType(szPath/*想要打开的文件名*/ , pOpenDocument/*已经打开的文档*/);  
  15.             //存储到目前位置最好的匹配  
  16.             if(match > bestMatch)  
  17.             {  
  18.                   bestMatch=match;  
  19.                   pBestTemplate=pTemplate;  
  20.             }  
  21.             //如果文件名与一个已经打开的文档对象匹配,则中断跳出  
  22.             if(match == CDocTemplate::yesAlreadyOpen)  
  23.                   break;  
  24.       }  
  25.       if(pOpenDocument != NULL)  
  26.       {  
  27.             //如果我们已经找到了一个已经打开的文档对象,则激活的它的框架窗口和视图,并返回它的地址  
  28.             //......  
  29.             //激活框架和视图的代码省略......  
  30.             //......  
  31.              
  32.             return pOpenDocument;  
  33.       }   
  34.        
  35.       //没有“最好”的模板,返回Error!  
  36.       if(pBestTemplate == NULL)  
  37.       {  
  38.             AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);  
  39.             return NULL;  
  40.       }  
  41.       //使用最匹配的文档模板对象打开给定的文件名  
  42.       return pBestTemplate->OpenDocumentFile(szPath);   
  43. }  

深入挖掘IDR_MAINFRAME

相信每个用过 MFC 的文档类的读者,特别是使用 Application Wizard 创建的单文档模板,都能在资源中的 String Table (字符串资源)中找到 IDR_MAINFRAME 这个 ID ,那么这是个什么呢,究竟有什么神奇的功效?待我们一步一步揭开它神秘的面纱。
 
 
首先,我们可以发现在字符串都是用很多 “\n” 隔开的,这些字符都用来表示什么呢?看来它的意义不简单啊,好的,我们使出第一招 ---MSDN

所谓期望越大,失望也就越大,在 MSDN 中竟然没有它的身影,不得不让人感到失落,那么它到底是何方神圣,连 生死薄 都没有,看来来历不小。
看来我们不得不使出第二招 --- 在代码中搜索,即使 MSDN 没有,但是在代码中总会调用它的,如果没调用它,我想也没有什么特殊的意义了。

  
功夫不负有心人啊,很快就找到它了,这段代码存在于 CWinApp::InitInstance ()中,于是我们不禁又要问 CSingleDocTemplate 又是什么类呢,我们 MSDN 它。
  
我们可以得到以下信息:

  
翻译一下,就是定义一个可以生效一个单文档接口的文档模板。它的基类就是 CDocTemplate
我们再看它的构造函数:

  
也就是说,在这个类里传进去四个参数,根据信息我们可以知道它们分别是字符串资源 ID ,文档类指针,窗口框架类指针,视图类指针。
我们重点关系第一个参数,先查一下 nIDResource 是用来干啥的,我们继续看。
 
 
翻译一下,指向一个与文档类型一起用的资源 ID ,它可能包括菜单,图标,加速键列表,字符串资源。这个字符串资源由多达用 ‘\n’ 隔离七个字符子链组成 ,这个字符描绘文档类型,更多信息请看 CDocTemplate::GetDocString()
哦,我们知道了一个大概了,这七个字符串子链都用来表示文档类型,那么又有问题了,它们都分别表示什么文档类型呢?我们把问题放在这里。(对于后面的三个参数,就是把它们连接起来,由于与本主题关系不大,所以就不深究了。)
现在,我们可以关心一下它的父类,特别是前面提到的那个成员函数 GetDocString() ,关于它的介绍如下:

 
翻译一下,获得一个被文档类型关联的字符串。
  
我们重点关心 index, 这是一个枚举量,总共有七个成员,有眉目了,这个七个成员想必就与前面提到的问题有关了,我们看描述。

大概说明一下:

CDocTemplate::windowTitle
  
主窗口标题栏上的字符串,(仅在 SDI 程序出现, MDI 程序将以 IDR_MAINFRAME 字符串为默认值。)
CDocTemplate::docName
  
缺省文档的名称。缺省是无标题。
CDocTemplate::fileNewName
  
文档类型的名称。如果应用程序支持多种类型的文档,此字符串将显示在 "File/New" 对话框中。如果没有指定,就不能够
CDocTemplate::filterName
  
文档类型的描述和一个适用于此类型的通配符过滤器。这个字符串将出现在 “File/Open” 对话框中的文件类型列表框中。要和 CDocTemplate::filterExt 一起使用。
CDocTemplate::filterExt
  
文档的扩展名。如果没有指定,就不能够在 “File/Open” 对话框中处理这种文档。要和 CDocTemplate::filterName 一起使用。
CDocTemplate::regFileTypeId
  
如果你以 ::RegisterShellFileTypes 向系统的注册表注册文件类型,此值会出现在 HKEY_CLASSES_ROOT 之下成为其子项,并仅供 Windows 内部使用。如果没有指定,这种文件类型就无法注册。
CDocTemplate::regFileTypeName
  
这也是存储在注册表中的文件类型名称。它会显示于程序中用以访问注册表的对话框内。



你可能感兴趣的:(MFC)