zoukankan      html  css  js  c++  java
  • MFC单文档视图穷追猛打 (转)

    MFC单文档视图穷追猛打 (转)

    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的人们会有帮助,当然上面的描述中肯定会有错误的地方和不足之处,希望阅读者指出,只有这样才能达到共同进步的目的。

  • 相关阅读:
    ionic localstorage
    angular 中文鏈接
    把jqmobi 變成jQuery 的插件 從此使用jQuery
    jqmobi 的一些設置
    ionic ngcordova map 地圖
    ionic pull to refresh 下拉更新頁面
    json 對象的序列化
    鍵盤彈出,頁面佈局被推上去了.....
    Cordova V3.0.0中config.xml配置文件的iOS Configuration
    android ios 只能輸入數字 不能輸入小數點的 函數 cordova
  • 原文地址:https://www.cnblogs.com/cy163/p/1906052.html
Copyright © 2011-2022 走看看