您的位置:首页 > 编程语言 > C语言/C++

MFC单文档视图穷追猛打

2014-08-27 17:51 501 查看
本文转自 http://blog.chinaunix.net/uid-790245-id-2037415.html

目录

第一章找回WinMain函数 2

第二章InitApplication()函数 2

第三章InitInstance ()函数 3

第一节:new CSingleDocTemplate 3

第二节:AddDocTemplate(pDocTemplate); 5

第三节:ProcessshellCommand(cmdInfo) 5

第一段AfxGetApp()->OnCmdMsg(…) 6

第二段OnFileNew(); 6

第四节 :m_pMainWnd->ShowWindow(SW_SHOW); ///显示窗口 8

第五节 :m_pMainWnd-> UpdateWindow (); ///重画窗口 8

第四章pApp->Run函数 8

第五章总结 8

  本文将针对一个单文档来描述MFC的文档/视图结构,他直接打开MFC的源代码进行分析,在分析过程中去掉了无关的部分。所以第一步就是要创建一个称为First得工程,文档类型是单文档,下文将围绕这个工程来讲的。

第一章找回WinMain函数

首先在VC的安装路径中找到WINMAIN.CPP文件,AfxWinMain函数就是VC编译器的入口,去掉一些不重要的东西后得到如下得程序

int AFXapi AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)

{

int nReturnCode = -1;

CWinApp* pApp = AfxGetApp(); ///获取应用程序类的指针与文档视图无关。

AfxWinInit(hInstance, hPrevInstance,lpCmdLine,nCmdShow); ///与文档视图无关。

pApp->InitApplication();  ///初始化应用程序详见下文。

pApp->InitInstance();  ///最重要下面祥述

nReturnCode = pApp->Run();///消息循环直到应用程序被关闭。与文档视图无关。

AfxWinTerm();  ///与文档视图无关。

return nReturnCode;///整个应用结束。

}

下面分节讲述。

第二章InitApplication()函数

在文件Appcore.cpp文件中InitApplication如下:

BOOL CWinApp::InitApplication()

{

 if (CDocManager::pStaticDocManager != NULL)  ///这段和我们关系不大,暂时不理它

 {

 if (m_pDocManager == NULL)

 m_pDocManager = CDocManager::pStaticDocManager;

 CDocManager::pStaticDocManager = NULL;

 }

 if (m_pDocManager != NULL)

 m_pDocManager->AddDocTemplate(NULL);

 else

 CDocManager::bStaticInit = FALSE;  /*我们的程序将会执行到这句,表示文档类用动态方式创建,也就是说用RUNTIME_CLASS来创建的(详见下文)。*/

 return TRUE;

}

  说明:CDocManager类是一个不公开的类,他主要用来管理多文档模板对象的,对于单文档只有一个文档模板这个类不是很重要,但还是用它管理文档模板的;本文只在相关之处作说明。bStaticInit是它的一个静态成员。

第三章InitInstance ()函数

  它一般被重载,在First工程中,InitInstance中和文档视图类有关的程序有下面的一些:

 CSingleDocTemplate* pDocTemplate;  ///定义指针

 pDocTemplate = new CSingleDocTemplate(

 IDR_MAINFRAME,

 RUNTIME_CLASS(CFirstDoc),

 RUNTIME_CLASS(CMainFrame),

 RUNTIME_CLASS(CFirstView));  ///这条语句的作用见第一段

 AddDocTemplate(pDocTemplate);

 CCommandLineInfo cmdInfo;  ///定义一个对象

 ParseCommandLine(cmdInfo);  ///解析命令行并发送参数,与文档视图无关

 if (!ProcessShellCommand(cmdInfo))  ///这是最重要的详见的三段

 return FALSE;

 m_pMainWnd->ShowWindow(SW_SHOW);  ///显示窗口

 m_pMainWnd->UpdateWindow();

第一节:new CSingleDocTemplate

new CSingleDocTemplate其实就是创建一个CSingleDocTemplate对象并调用他的构造函数,要讲清楚这句话,首先必须明白RUNTIME_CLASS结构,RUNTIME_CLASS结构定义如下:

#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

于是这句话展开后如下(关于符合##的具体意义参见MSDN):

 pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

(CRuntimeClass*)(&CFirstDoc::classCFirstDoc),

(CRuntimeClass*)(&CMainFrame::classCMainFrame),

(CRuntimeClass*)(&CFirstView::classCFirstView));

这时我们会发现CfirstDoc,CmainFrame,CfirstView各会多冒出一个静态成员出来,它究竟在哪里呢?查看这三个类的定义我们会发现每个类定义都有一个宏DECLARE_DYNCREATE,在实现文件中有另外一个宏IMPLEMENT_DYNCREATE。下面把这些宏的展开情况列出如下:

(1)文档类

DECLARE_DYNCREATE(CFirstDoc) 展开后为:

public:  

 static const CRuntimeClass classCFirstDoc; ///这就是上面所说的那个静态成员

 virtual CRuntimeClass* GetRuntimeClass() const; 

 static Cobject* PASCAL CreateObject();

IMPLEMENT_DYNCREATE(CFirstDoc, CDocument)  展开后为:

 CObject* PASCAL CFirstDoc::CreateObject()

 { return new CFirstDoc; }

 const CRuntimeClass CFirstDoc::classCFirstDoc= { 

 "CFirstDoc", sizeof(class CFirstDoc), 0xFFFF, CFirstDoc::CreateObject,  (CRu

ntimeClass*)(&CDocument::classCDocument), NULL };

 CRuntimeClass* CFirstDoc::GetRuntimeClass() const 

 { return (CRuntimeClass*)(&CFirstDoc::classCFirstDoc); } 

(2)视图类

DECLARE_DYNCREATE(CFirstView)  展开后为:

public:  

 static const CRuntimeClass classCFirstView; 

 virtual CRuntimeClass* GetRuntimeClass() const; 

 static CObject* PASCAL CreateObject();

IMPLEMENT_DYNCREATE(CFirstView, CView)  展开后为:

 CObject* PASCAL CFirstView::CreateObject()

 { return new CFirstView; }

 const CRuntimeClass CFirstView::classCFirstView = { 

 " CFirstView ", sizeof(class CFirstView), 0xFFFF, CFirstView::CreateObject,  (CRu

ntimeClass*)(&CView::classCView), NULL };

 CRuntimeClass* CFirstView::GetRuntimeClass() const 

 { return (CRuntimeClass*)(& CFirstView::classCFirstView); } 

(3)主框架

DECLARE_DYNCREATE(CMainFrame)  展开后为:

public:  

 static const CRuntimeClass classCMainFrame; 

 virtual CRuntimeClass* GetRuntimeClass() const; 

 static CObject* PASCAL CreateObject();

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)  展开后为:

 CObject* PASCAL CMainFrame::CreateObject()

 { return new CMainFrame; }

 const CRuntimeClass CMainFrame::classCMainFrame = { 

 "CMainFrame", sizeof(class CMainFrame), 0xFFFF, CMainFrame::CreateObject,  (CRu

ntimeClass*)(& CFrameWnd::classCFrameWnd), NULL };

 CRuntimeClass* CMainFrame::GetRuntimeClas
11049
s() const 

 { return (CRuntimeClass*)(&CMainFrame::classCMainFrame); }

  这些宏首先在你的定义文件中定义一个以class+类名为名字的静态变量。然后定义一个返回这个静态变量的函数GetRuntimeClass并在实现文件中实现之。最后定义并实现一个创建对象的函数CreateObject。

好了,我们现在就来看看CSingleDocTemplate对象的构造函数做了些什么:下面是他的实现

CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource,

 CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,

 CRuntimeClass* pViewClass)

 : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)

{

 m_pOnlyDoc = NULL;

}

它主要是初始化他的父类CDocTemplate,CDocTemplate做了些什么?如下:

{

<1>把资源ID及文档、框架、视图的三个CRuntimeClass结构的静态成员的地址(也就是传进来的四个参数) 保存了起来。

<2>LoadTemplate();///为指定的文档模板对象装载资源

}

至此这个new语句总算讲完了。

第二节:AddDocTemplate(pDocTemplate);

下面是他的实现函数

void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)

{

 if (m_pDocManager == NULL)

m_pDocManager = new CDocManager; /*创建一个文档管理类,对于单文档常常只有一个文档模板用CDocManager类并不是很重要,我们可以忽略这句*/

 m_pDocManager->AddDocTemplate(pTemplate); /*把文档模板指针保存在CDocManager实例中由CDocManager去管理他,我们只要知道有这个事就行了*/

}

第三节:ProcessShellCommand(cmdInfo)

  这个函数发送的一个命令就是FileNew其他的暂时不管他,其实所有的文档类,框架类,视图类都是在这个函数中创建的,在第一段中已经讲到应用程序类将上三个类的CRuntimeClass一个静态成员地址保存起来了,关于CRuntimeClass结构的具体功能参照MSDN。

ProcessShellCommand描述如下:

BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)

{

 BOOL bResult = TRUE;

 switch (rCmdInfo.m_nShellCommand)

 {

 case CCommandLineInfo::FileNew:

 if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))

 OnFileNew();

 if (m_pMainWnd == NULL)

 bResult = FALSE;

 break;

 case CCommandLineInfo::FileOpen:

 if (!OpenDocumentFile(rCmdInfo.m_strFileName))

 bResult = FALSE;

 break;

……………………………….///还有很多消息处理略去

 }

 return bResult;

}

第一段AfxGetApp()->OnCmdMsg(…)

AfxGetApp()->OnCmdMsg主要是派发命令消息,跟我们关系最大的消息是菜单命令ID_FILE_NEW,下面讲述他的处理函数OnFileNew

第二段OnFileNew();

 OnFileNew只是简单的调用文档管理对象的同名函数,他的描述如下:

void CWinApp::OnFileNew()

{

 if (m_pDocManager != NULL)

 m_pDocManager->OnFileNew();///只是调用CDocManager::OnFileNew

}

文档管理类的OnFileNew去掉一些没用的东西后实现如下:

void CDocManager::OnFileNew()

{

 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();/*获得第一个文档模板,对于单文档只有一个文档模板*/

 if (m_templateList.GetCount() > 1)///对单文档这段没用

 {

 CNewTypeDlg dlg(&m_templateList);

 int nID = dlg.domodal();

 if (nID == IDOK)

 pTemplate = dlg.m_pselectedTemplate;

 else

 return;  /// none - cancel operation

 }

 pTemplate->OpenDocumentFile(NULL);/*其实是调用CSingleDocTemplate::OpenDocumentFile函数下面祥述这里,因为在CDocTemplate 类中这个函数是一个纯虚函数*/

}

下面简述pTemplate->OpenDocumentFile(NULL);这个函数,

这是最重要的一个函数,他执行了如下6个步骤:

<1>pDocument = CreateNewDocument();///创建一个新文档

  {

CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();/*这是才真正的创建文档对象,这里创建的文档对象就是上面RUNTIME_CLASS(CFirstDoc),中的对象这是MFC开发人员想出来的一种运行时创建对象的方法,应该说比较不错的,不知道C++Builder的是怎样构造的。*/

 AddDocument(pDocument);  ///把创建的对象指针记录下来,以后还要用的。

}

<2> pFrame = CreateNewFrame(pDocument, NULL);  /*根据新文档指针创建新文档框架,这个比较重要下一段描述*/

<3>SetDefaultTitle(pDocument);  ///设置缺省标题,不重要的

<4>pDocument->OnNewDocument()  ///清除老文档内容

{

 DeleteContents();  ///删除原来的内容而不管是否存盘

 SetModifiedFlag(FALSE);  ///清除曾经修改过的标记

}

<5>pThread->m_pMainWnd = pFrame;  ///框架窗口作为的指针作为主窗口的指针

<.6>InitialUpdateFrame(pFrame, pDocument, bMakeVisible);  /*使框架中的视图窗口接受到OnInitialUpdat的调用,并作原始更新。*/

至此第一段的OnFileNew函数总算讲完。下面讲<2> CreateNewFrame函数

CreateNewFrame是CDocTemplate的成员函数如下3个步骤:

<2.1>CCreateContext context;  ///创建CcreateContext对象.也就是我们重载LoadFrame函数的那个CcreateContext的来源*/

context.m_pCurrentFrame = pOther;  ///保存当前框架,一般为NULL

context.m_pCurrentDoc = pDoc;  ///保存文档对象指针

context.m_pNewViewClass = m_pViewClass;  ///保存视图对象指针

context.m_pNewDocTemplate = this;  ///保存文档模板的指针。

<2.2>CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); ///真正创建框架的对象

<2.3>pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,  

 NULL, &context)  ///加载资源,这是最重要的,下面讲述

{

<2.3.1> GetIconWndClass(dwDefaultStyle, nIDResource)  ///加载资源并注册窗口

 {

 AfxFindResourceHandle

 ::LoadIcon  ///加载光标资源

 CREATESTRUCT cs;  ///创建一个CREATESTRUCT结构实例

 PreCreateWindow(cs);  /*调用窗口预创建函数,这个函数我们可以重载的

 根据cs调用 AfxRegisterWndClass注册框架窗口*/

}

<2.3.2> Create(…)  ///创建框架窗口,后面祥述,此处暂时不讲。

<2.3.3> m_hMenuDefault = ::GetMenu(m_hWnd);  ///获取并保存菜单句柄

<2.3.4> LoadAccelTable(MAKEINTRESOURCE(nIDResource));  ///装载加速键表

}

 下面讲Create函数,如下4个步骤:

 <2.3.2.1>hMenu = ::LoadMenu(hInst, lpszMenuName);  ///加载菜单

<2.3.2.2>CREATESTRUCT cs;  ///创建一个CREATESTRUCT结构实例

cs.lpCreateParams = lpParam;  ///(此时的lpParam就是<2.1>建的context对象指针)

<2.3.2.3> PreCreateWindow(cs);  ///调用窗口预创建函数,这个函数我们可以重载的,又调用一次

<2.3.2.4>调用API函数::CreateWindowEx真正创建框架窗口,创建过程中会发出WM_CREATE和WM_NCCREATE等消息。

下面讲接收WM_CREATE消息的消息处理函数CMainFrame::OnCreate,其他消息不重要

<2.3.2.3.1>CMainFrame::OnCreate最重要的是调用CFrameWnd::OnCreateHelper函数如下:

{

<1>CWnd::OnCreate(lpcs);  ///调用缺省的

<2>调用可重载函数OnCreateClient(lpcs,pContext)函数, 在OnCreateClient函数内只是调用CreateView(pContext, AFX_IDW_PANE_FIRST)函数; 

CreateView函数步骤如下:

{

<2.1> pView=pContext->m_pNewViewClass->CreateObject(); /*创建视图对象*/

<2.2>pView->Create(…);/*调用视图对象的Create函数真正创建视图,其更详细的动作可以不理他,但要知道这里会发生很多事情(跟创建框架窗口类似的);其他一些不重要的动作可以不理他;*/

}

<3>PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);/*发一个WM_SETMESSAGESTRING消息初始化消息串*/

<4>RecalcLayout();  ///重定控制栏等无关动作

}

<2.3.2.3.2> m_wndToolBar.CreateEx;  ///创建工具栏

<2.3.2.3.3> LoadToolBar;  /停靠工具栏等无关动作

至此WM_CREATE消息处理讲完,同时第三段的ProcessShellCommand函数也结束了

 

第四节 :m_pMainWnd->ShowWindow(SW_SHOW); ///显示窗口

第五节 :m_pMainWnd-> UpdateWindow (); ///重画窗口

至此第二节InitInstance ()函数讲完,

第四章pApp->Run函数

Run函数处理消息循环直到程序推出,已经有很多书籍讲到他的过程,本文不描述他。

第五章总结

从上面的描述中可以看出,文档/视图结构的关键在CRuntimeClass结构,通过CRuntimeClass结构实现动态创建对象。

罗里罗嗦讲了这么多,希望对学MFC的人们会有帮助,当然上面的描述中肯定会有错误的地方和不足之处,希望阅读者指出,只有这样才能达到共同进步的目的。

目录

第一章找回WinMain函数 2

第二章InitApplication()函数 2

第三章InitInstance ()函数 3

第一节:new CSingleDocTemplate 3

第二节:AddDocTemplate(pDocTemplate); 5

第三节:ProcessshellCommand(cmdInfo) 5

第一段AfxGetApp()->OnCmdMsg(…) 6

第二段OnFileNew(); 6

第四节 :m_pMainWnd->ShowWindow(SW_SHOW); ///显示窗口 8

第五节 :m_pMainWnd-> UpdateWindow (); ///重画窗口 8

第四章pApp->Run函数 8

第五章总结 8

  本文将针对一个单文档来描述MFC的文档/视图结构,他直接打开MFC的源代码进行分析,在分析过程中去掉了无关的部分。所以第一步就是要创建一个称为First得工程,文档类型是单文档,下文将围绕这个工程来讲的。

第一章找回WinMain函数

首先在VC的安装路径中找到WINMAIN.CPP文件,AfxWinMain函数就是VC编译器的入口,去掉一些不重要的东西后得到如下得程序

int AFXapi AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)

{

int nReturnCode = -1;

CWinApp* pApp = AfxGetApp(); ///获取应用程序类的指针与文档视图无关。

AfxWinInit(hInstance, hPrevInstance,lpCmdLine,nCmdShow); ///与文档视图无关。

pApp->InitApplication();  ///初始化应用程序详见下文。

pApp->InitInstance();  ///最重要下面祥述

nReturnCode = pApp->Run();///消息循环直到应用程序被关闭。与文档视图无关。

AfxWinTerm();  ///与文档视图无关。

return nReturnCode;///整个应用结束。

}

下面分节讲述。

第二章InitApplication()函数

在文件Appcore.cpp文件中InitApplication如下:

BOOL CWinApp::InitApplication()

{

 if (CDocManager::pStaticDocManager != NULL)  ///这段和我们关系不大,暂时不理它

 {

 if (m_pDocManager == NULL)

 m_pDocManager = CDocManager::pStaticDocManager;

 CDocManager::pStaticDocManager = NULL;

 }

 if (m_pDocManager != NULL)

 m_pDocManager->AddDocTemplate(NULL);

 else

 CDocManager::bStaticInit = FALSE;  /*我们的程序将会执行到这句,表示文档类用动态方式创建,也就是说用RUNTIME_CLASS来创建的(详见下文)。*/

 return TRUE;

}

  说明:CDocManager类是一个不公开的类,他主要用来管理多文档模板对象的,对于单文档只有一个文档模板这个类不是很重要,但还是用它管理文档模板的;本文只在相关之处作说明。bStaticInit是它的一个静态成员。

第三章InitInstance ()函数

  它一般被重载,在First工程中,InitInstance中和文档视图类有关的程序有下面的一些:

 CSingleDocTemplate* pDocTemplate;  ///定义指针

 pDocTemplate = new CSingleDocTemplate(

 IDR_MAINFRAME,

 RUNTIME_CLASS(CFirstDoc),

 RUNTIME_CLASS(CMainFrame),

 RUNTIME_CLASS(CFirstView));  ///这条语句的作用见第一段

 AddDocTemplate(pDocTemplate);

 CCommandLineInfo cmdInfo;  ///定义一个对象

 ParseCommandLine(cmdInfo);  ///解析命令行并发送参数,与文档视图无关

 if (!ProcessShellCommand(cmdInfo))  ///这是最重要的详见的三段

 return FALSE;

 m_pMainWnd->ShowWindow(SW_SHOW);  ///显示窗口

 m_pMainWnd->UpdateWindow();

第一节:new CSingleDocTemplate

new CSingleDocTemplate其实就是创建一个CSingleDocTemplate对象并调用他的构造函数,要讲清楚这句话,首先必须明白RUNTIME_CLASS结构,RUNTIME_CLASS结构定义如下:

#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

于是这句话展开后如下(关于符合##的具体意义参见MSDN):

 pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

(CRuntimeClass*)(&CFirstDoc::classCFirstDoc),

(CRuntimeClass*)(&CMainFrame::classCMainFrame),

(CRuntimeClass*)(&CFirstView::classCFirstView));

这时我们会发现CfirstDoc,CmainFrame,CfirstView各会多冒出一个静态成员出来,它究竟在哪里呢?查看这三个类的定义我们会发现每个类定义都有一个宏DECLARE_DYNCREATE,在实现文件中有另外一个宏IMPLEMENT_DYNCREATE。下面把这些宏的展开情况列出如下:

(1)文档类

DECLARE_DYNCREATE(CFirstDoc) 展开后为:

public:  

 static const CRuntimeClass classCFirstDoc; ///这就是上面所说的那个静态成员

 virtual CRuntimeClass* GetRuntimeClass() const; 

 static Cobject* PASCAL CreateObject();

IMPLEMENT_DYNCREATE(CFirstDoc, CDocument)  展开后为:

 CObject* PASCAL CFirstDoc::CreateObject()

 { return new CFirstDoc; }

 const CRuntimeClass CFirstDoc::classCFirstDoc= { 

 "CFirstDoc", sizeof(class CFirstDoc), 0xFFFF, CFirstDoc::CreateObject,  (CRu

ntimeClass*)(&CDocument::classCDocument), NULL };

 CRuntimeClass* CFirstDoc::GetRuntimeClass() const 

 { return (CRuntimeClass*)(&CFirstDoc::classCFirstDoc); } 

(2)视图类

DECLARE_DYNCREATE(CFirstView)  展开后为:

public:  

 static const CRuntimeClass classCFirstView; 

 virtual CRuntimeClass* GetRuntimeClass() const; 

 static CObject* PASCAL CreateObject();

IMPLEMENT_DYNCREATE(CFirstView, CView)  展开后为:

 CObject* PASCAL CFirstView::CreateObject()

 { return new CFirstView; }

 const CRuntimeClass CFirstView::classCFirstView = { 

 " CFirstView ", sizeof(class CFirstView), 0xFFFF, CFirstView::CreateObject,  (CRu

ntimeClass*)(&CView::classCView), NULL };

 CRuntimeClass* CFirstView::GetRuntimeClass() const 

 { return (CRuntimeClass*)(& CFirstView::classCFirstView); } 

(3)主框架

DECLARE_DYNCREATE(CMainFrame)  展开后为:

public:  

 static const CRuntimeClass classCMainFrame; 

 virtual CRuntimeClass* GetRuntimeClass() const; 

 static CObject* PASCAL CreateObject();

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)  展开后为:

 CObject* PASCAL CMainFrame::CreateObject()

 { return new CMainFrame; }

 const CRuntimeClass CMainFrame::classCMainFrame = { 

 "CMainFrame", sizeof(class CMainFrame), 0xFFFF, CMainFrame::CreateObject,  (CRu

ntimeClass*)(& CFrameWnd::classCFrameWnd), NULL };

 CRuntimeClass* CMainFrame::GetRuntimeClass() const 

 { return (CRuntimeClass*)(&CMainFrame::classCMainFrame); }

  这些宏首先在你的定义文件中定义一个以class+类名为名字的静态变量。然后定义一个返回这个静态变量的函数GetRuntimeClass并在实现文件中实现之。最后定义并实现一个创建对象的函数CreateObject。

好了,我们现在就来看看CSingleDocTemplate对象的构造函数做了些什么:下面是他的实现

CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource,

 CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,

 CRuntimeClass* pViewClass)

 : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)

{

 m_pOnlyDoc = NULL;

}

它主要是初始化他的父类CDocTemplate,CDocTemplate做了些什么?如下:

{

<1>把资源ID及文档、框架、视图的三个CRuntimeClass结构的静态成员的地址(也就是传进来的四个参数) 保存了起来。

<2>LoadTemplate();///为指定的文档模板对象装载资源

}

至此这个new语句总算讲完了。

第二节:AddDocTemplate(pDocTemplate);

下面是他的实现函数

void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)

{

 if (m_pDocManager == NULL)

m_pDocManager = new CDocManager; /*创建一个文档管理类,对于单文档常常只有一个文档模板用CDocManager类并不是很重要,我们可以忽略这句*/

 m_pDocManager->AddDocTemplate(pTemplate); /*把文档模板指针保存在CDocManager实例中由CDocManager去管理他,我们只要知道有这个事就行了*/

}

第三节:ProcessShellCommand(cmdInfo)

  这个函数发送的一个命令就是FileNew其他的暂时不管他,其实所有的文档类,框架类,视图类都是在这个函数中创建的,在第一段中已经讲到应用程序类将上三个类的CRuntimeClass一个静态成员地址保存起来了,关于CRuntimeClass结构的具体功能参照MSDN。

ProcessShellCommand描述如下:

BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)

{

 BOOL bResult = TRUE;

 switch (rCmdInfo.m_nShellCommand)

 {

 case CCommandLineInfo::FileNew:

 if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))

 OnFileNew();

 if (m_pMainWnd == NULL)

 bResult = FALSE;

 break;

 case CCommandLineInfo::FileOpen:

 if (!OpenDocumentFile(rCmdInfo.m_strFileName))

 bResult = FALSE;

 break;

……………………………….///还有很多消息处理略去

 }

 return bResult;

}

第一段AfxGetApp()->OnCmdMsg(…)

AfxGetApp()->OnCmdMsg主要是派发命令消息,跟我们关系最大的消息是菜单命令ID_FILE_NEW,下面讲述他的处理函数OnFileNew

第二段OnFileNew();

 OnFileNew只是简单的调用文档管理对象的同名函数,他的描述如下:

void CWinApp::OnFileNew()

{

 if (m_pDocManager != NULL)

 m_pDocManager->OnFileNew();///只是调用CDocManager::OnFileNew

}

文档管理类的OnFileNew去掉一些没用的东西后实现如下:

void CDocManager::OnFileNew()

{

 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();/*获得第一个文档模板,对于单文档只有一个文档模板*/

 if (m_templateList.GetCount() > 1)///对单文档这段没用

 {

 CNewTypeDlg dlg(&m_templateList);

 int nID = dlg.domodal();

 if (nID == IDOK)

 pTemplate = dlg.m_pselectedTemplate;

 else

 return;  /// none - cancel operation

 }

 pTemplate->OpenDocumentFile(NULL);/*其实是调用CSingleDocTemplate::OpenDocumentFile函数下面祥述这里,因为在CDocTemplate 类中这个函数是一个纯虚函数*/

}

下面简述pTemplate->OpenDocumentFile(NULL);这个函数,

这是最重要的一个函数,他执行了如下6个步骤:

<1>pDocument = CreateNewDocument();///创建一个新文档

  {

CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();/*这是才真正的创建文档对象,这里创建的文档对象就是上面RUNTIME_CLASS(CFirstDoc),中的对象这是MFC开发人员想出来的一种运行时创建对象的方法,应该说比较不错的,不知道C++Builder的是怎样构造的。*/

 AddDocument(pDocument);  ///把创建的对象指针记录下来,以后还要用的。

}

<2> pFrame = CreateNewFrame(pDocument, NULL);  /*根据新文档指针创建新文档框架,这个比较重要下一段描述*/

<3>SetDefaultTitle(pDocument);  ///设置缺省标题,不重要的

<4>pDocument->OnNewDocument()  ///清除老文档内容

{

 DeleteContents();  ///删除原来的内容而不管是否存盘

 SetModifiedFlag(FALSE);  ///清除曾经修改过的标记

}

<5>pThread->m_pMainWnd = pFrame;  ///框架窗口作为的指针作为主窗口的指针

<.6>InitialUpdateFrame(pFrame, pDocument, bMakeVisible);  /*使框架中的视图窗口接受到OnInitialUpdat的调用,并作原始更新。*/

至此第一段的OnFileNew函数总算讲完。下面讲<2> CreateNewFrame函数

CreateNewFrame是CDocTemplate的成员函数如下3个步骤:

<2.1>CCreateContext context;  ///创建CcreateContext对象.也就是我们重载LoadFrame函数的那个CcreateContext的来源*/

context.m_pCurrentFrame = pOther;  ///保存当前框架,一般为NULL

context.m_pCurrentDoc = pDoc;  ///保存文档对象指针

context.m_pNewViewClass = m_pViewClass;  ///保存视图对象指针

context.m_pNewDocTemplate = this;  ///保存文档模板的指针。

<2.2>CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); ///真正创建框架的对象

<2.3>pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,  

 NULL, &context)  ///加载资源,这是最重要的,下面讲述

{

<2.3.1> GetIconWndClass(dwDefaultStyle, nIDResource)  ///加载资源并注册窗口

 {

 AfxFindResourceHandle

 ::LoadIcon  ///加载光标资源

 CREATESTRUCT cs;  ///创建一个CREATESTRUCT结构实例

 PreCreateWindow(cs);  /*调用窗口预创建函数,这个函数我们可以重载的

 根据cs调用 AfxRegisterWndClass注册框架窗口*/

}

<2.3.2> Create(…)  ///创建框架窗口,后面祥述,此处暂时不讲。

<2.3.3> m_hMenuDefault = ::GetMenu(m_hWnd);  ///获取并保存菜单句柄

<2.3.4> LoadAccelTable(MAKEINTRESOURCE(nIDResource));  ///装载加速键表

}

 下面讲Create函数,如下4个步骤:

 <2.3.2.1>hMenu = ::LoadMenu(hInst, lpszMenuName);  ///加载菜单

<2.3.2.2>CREATESTRUCT cs;  ///创建一个CREATESTRUCT结构实例

cs.lpCreateParams = lpParam;  ///(此时的lpParam就是<2.1>建的context对象指针)

<2.3.2.3> PreCreateWindow(cs);  ///调用窗口预创建函数,这个函数我们可以重载的,又调用一次

<2.3.2.4>调用API函数::CreateWindowEx真正创建框架窗口,创建过程中会发出WM_CREATE和WM_NCCREATE等消息。

下面讲接收WM_CREATE消息的消息处理函数CMainFrame::OnCreate,其他消息不重要

<2.3.2.3.1>CMainFrame::OnCreate最重要的是调用CFrameWnd::OnCreateHelper函数如下:

{

<1>CWnd::OnCreate(lpcs);  ///调用缺省的

<2>调用可重载函数OnCreateClient(lpcs,pContext)函数, 在OnCreateClient函数内只是调用CreateView(pContext, AFX_IDW_PANE_FIRST)函数; 

CreateView函数步骤如下:

{

<2.1> pView=pContext->m_pNewViewClass->CreateObject(); /*创建视图对象*/

<2.2>pView->Create(…);/*调用视图对象的Create函数真正创建视图,其更详细的动作可以不理他,但要知道这里会发生很多事情(跟创建框架窗口类似的);其他一些不重要的动作可以不理他;*/

}

<3>PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);/*发一个WM_SETMESSAGESTRING消息初始化消息串*/

<4>RecalcLayout();  ///重定控制栏等无关动作

}

<2.3.2.3.2> m_wndToolBar.CreateEx;  ///创建工具栏

<2.3.2.3.3> LoadToolBar;  /停靠工具栏等无关动作

至此WM_CREATE消息处理讲完,同时第三段的ProcessShellCommand函数也结束了

 

第四节 :m_pMainWnd->ShowWindow(SW_SHOW); ///显示窗口

第五节 :m_pMainWnd-> UpdateWindow (); ///重画窗口

至此第二节InitInstance ()函数讲完,

第四章pApp->Run函数

Run函数处理消息循环直到程序推出,已经有很多书籍讲到他的过程,本文不描述他。

第五章总结

从上面的描述中可以看出,文档/视图结构的关键在CRuntimeClass结构,通过CRuntimeClass结构实现动态创建对象。

罗里罗嗦讲了这么多,希望对学MFC的人们会有帮助,当然上面的描述中肯定会有错误的地方和不足之处,希望阅读者指出,只有这样才能达到共同进步的目的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 编程