MFC技术内幕系列之(二)---MFC文档视图结构内幕

       引言:侯捷老师的"深入浅出MFC"一书的第8章中有“"Document/View"是MFC的基石。”一说,可以看出文档视图结构在MFC Framework中的地位是多么的重要。本文将以一个标准MFC应用程序向导作成的MDI程序为例,来和大家一起详细挖掘文档视图结构的内幕。

正文:
                       /////////////////////////////////////////////
                       /*  1.回顾"InitInstance函数"  */
                       /////////////////////////////////////////////
   在我的《MFC应用程序“生死因果”内幕》一文中,当谈到CMyWinApp::InitInstance()时,我只是粗略的讲了介绍了一下各个函数的功能,而忽略了很多细节,这里让我们在回顾一下CMyWinApp::InitInstance()函数,并将里面与文档视图结构有关的代码深入探讨一下:
   BOOL CMyApp::InitInstance()//只列出了与文档视图结构相关的源代码
  {
 //...

 //文档模板将用作文档、框架窗口和视图之间的连接
 CMultiDocTemplate* pDocTemplate;
 pDocTemplate = new CMultiDocTemplate(IDR_MyTYPE,
  RUNTIME_CLASS(CMyDoc),
  RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
  RUNTIME_CLASS(CMyView));
 AddDocTemplate(pDocTemplate);
 // 创建主 MDI 框架窗口
 CMainFrame* pMainFrame = new CMainFrame;
 if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
  return FALSE;
 m_pMainWnd = pMainFrame;
 // 仅当具有后缀时才调用 DragAcceptFiles
 //  在 MDI 应用程序中,这应在设置 m_pMainWnd 之后立即发生
 // 分析标准外壳命令、DDE、打开文件操作的命令行
 CCommandLineInfo cmdInfo;
 ParseCommandLine(cmdInfo);
 // 调度在命令行中指定的命令。如果
 // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
 if (!ProcessShellCommand(cmdInfo))
  return FALSE;
 // 主窗口已初始化,因此显示它并对其进行更新
 pMainFrame->ShowWindow(m_nCmdShow);
 pMainFrame->UpdateWindow();
 return TRUE;
 }

                       ////////////////////////////////////////////
                       /*     2.初始化文档模板       */
                       ////////////////////////////////////////////
分析以下代码:
        CMultiDocTemplate* pDocTemplate;
 pDocTemplate = new CMultiDocTemplate(IDR_MyTYPE,
  RUNTIME_CLASS(CMyDoc),
  RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
  RUNTIME_CLASS(CMyView));
    应用程序首先实例化一个CMultiDocTemplate对象,此过程也是对CMultiDocTemplate类数据成员的初始化过程。按调用次序我列出了以下源代码:
    注释1: CDocTemplate构造函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/doctempl.cpp
 CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
           CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)//部分源代码
 {
 ASSERT_VALID_IDR(nIDResource);
 ASSERT(pDocClass == NULL ||
  pDocClass->IsDerivedFrom(RUNTIME_CLASS(CDocument)));
 ASSERT(pFrameClass == NULL ||
  pFrameClass->IsDerivedFrom(RUNTIME_CLASS(CFrameWnd)));
 ASSERT(pViewClass == NULL ||
  pViewClass->IsDerivedFrom(RUNTIME_CLASS(CView)));

 m_nIDResource = nIDResource;
 ...//
        m_pDocClass = pDocClass;
 m_pFrameClass = pFrameClass;
 m_pViewClass = pViewClass;
 m_pOleFrameClass = NULL;
 m_pOleViewClass = NULL;
        ...//
 }
  以上为CMultiDocTemplate类的基类CDocTemplate构造函数的部分源代码,该函数初始化了四个重要的成员
m_nIDResource,m_pDocClass,m_pFrameClass和m_pViewClass。
   注释2: CMultiDocTemplate构造函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/docmulti.cpp
   CMultiDocTemplate::CMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
 CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
  : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)
  {
 ASSERT(m_docList.IsEmpty());

 m_hMenuShared = NULL;
 m_hAccelTable = NULL;
 m_nUntitledCount = 0;   // start at 1

 // load resources in constructor if not statically allocated
 if (!CDocManager::bStaticInit)
  LoadTemplate();
  }
   看完以上代码后,来回过头看一看InitInstance函数将什么参数值传给了CMultiDocTemplate的构造函数。
原来是一些RUNTIME_CLASS宏。以下是RUNTIME_CLASS宏的定义:
   注释3: 以下的宏定义在:../Visual Studio.NET/vc7/atlmfc/include/afx.h中
   #define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
   #define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
   这个地方是个难点,这将涉及到MFC的另一个重要技术---"执行期类型识别"。此项技术我将在
MFC技术内幕系列之(三)---《MFC执行期类型识别与动态创建技术内幕》中详细讲解。回到眼前来,源代码中这样作是为了将CMyDoc,CChildFrame,CMyView各类中的static CRuntimeClass class##class_name地址赋予CMultiDocTemplate类的各CRuntimeClass*指针成员m_pDocClass,m_pFrameClass和m_pViewClass,这位以后的动态创建Document/Frame/View"三口组"打下了基础。
                       ///////////////////////////////////////////
                       /*       3.文档模板列队       */
                       ///////////////////////////////////////////
   文档模板初始化结束后,InitInstance函数调用了CWinApp::AddDocTemplate(pDocTemplate)函数,其主要目的是将以初始化后的那个文档模板加入到文档模板链表中,并由CDocManager类对象进行管理。以下操作就是为了完成此工作。

注释1: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/appui2.cpp
void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)//将CMultiDocTemplate* pDocTemplate
{                                                    //传给pTemplate
 if (m_pDocManager == NULL)
  m_pDocManager = new CDocManager;
 m_pDocManager->AddDocTemplate(pTemplate);
}
注释2: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/docmgr.cpp
 CDocManager::CDocManager()
 {
  }//目前是一个空函数;

 void CDocManager::AddDocTemplate(CDocTemplate* pTemplate)//部分源代码
 {
 if (pTemplate == NULL)
 {
  ...//
 }
 else
 {
  ASSERT_VALID(pTemplate);
  ASSERT(m_templateList.Find(pTemplate, NULL) == NULL);// must not be in list
  pTemplate->LoadTemplate();
  m_templateList.AddTail(pTemplate);//CPtrList m_templateList is a member                                                             //of CDocManager
 }
 }

                       ///////////////////////////////////////////////
                       /*    4.创建程序主框架窗口    */
                       ///////////////////////////////////////////////
    应用程序实例化了一个CMainFrame类对象,并调用LoadFrame函数加载窗口资源创建主框架窗口。以下是创建主框架窗口的流程。
    创建窗口的主要代码是:pMainFrame->LoadFrame(IDR_MAINFRAME);LoadFrame函数是MFC包装了窗口创建过程的函数,在后面动态创建Child窗口时,它还将披挂上阵(但稍有不同)。下面是它的源代码,让我们仔细分析一下:
  注释1: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/winmdi.cpp
   
  BOOL CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
 CWnd* pParentWnd, CCreateContext* pContext)          
 {
 if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle,
   pParentWnd, pContext))
  return FALSE;

 // save menu to use when no active MDI child window is present
 ASSERT(m_hWnd != NULL);
 m_hMenuDefault = ::GetMenu(m_hWnd);
 if (m_hMenuDefault == NULL)
  TRACE(traceAppMsg, 0, "Warning: CMDIFrameWnd without a default menu./n");
 return TRUE;
 }
  CMDIFrameWnd::LoadFrame调用了其基类CFrameWnd的LoadFrame,并将参数原封不动的传给它。

  注释2: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/winfrm.cpp
 BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
 CWnd* pParentWnd, CCreateContext* pContext)          //部分源代码
 {
 ...//
 CString strFullString;
 if (strFullString.LoadString(nIDResource))
  AfxExtractSubString(m_strTitle, strFullString, 0);    // first sub-string

 VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

 // attempt to create the window
 LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
 CString strTitle = m_strTitle;
 if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,
   pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))
 {
  return FALSE;   // will self destruct on failure normally
 }

 ...//
 if (pContext == NULL)   // send initial update
  SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);

 return TRUE;
 }
                       //////////////////////////////////////////////////////
                       /* 4.1注册应用程序主框架窗口类 */
                       //////////////////////////////////////////////////////

我自已架设了博客,文章已转到个人博客,欢迎交流!

MFC技术内幕系列之(二)---MFC文档视图结构内幕
http://www.jeanva.cn/post/52.html

你可能感兴趣的:(框架,null,Class,文档,mfc,Constructor)