posts - 134,comments - 22,trackbacks - 0

          MFC單文檔/視圖結(jié)構(gòu)窮追猛打

          目錄

          第一章找回WinMain函數(shù) 2
          第二章InitApplication()函數(shù) 2
          第三章InitInstance ()函數(shù) 3
          第一節(jié):new CSingleDocTemplate 3
          第二節(jié):AddDocTemplate(pDocTemplate); 5
          第三節(jié):ProcessShellCommand(cmdInfo) 5
          第一段AfxGetApp()->OnCmdMsg(…) 6
          第二段OnFileNew(); 6
          第四節(jié) :m_pMainWnd->ShowWindow(SW_SHOW); ///顯示窗口 8
          第五節(jié) :m_pMainWnd-> UpdateWindow (); ///重畫窗口 8
          第四章pApp->Run函數(shù) 8
          第五章總結(jié) 8

            本文將針對一個單文檔來描述MFC的文檔/視圖結(jié)構(gòu),他直接打開MFC的源代碼進行分析,在分析過程中去掉了無關(guān)的部分。所以第一步就是要創(chuàng)建一個稱為First得工程,文檔類型是單文檔,下文將圍繞這個工程來講的。
          第一章找回WinMain函數(shù)
          首先在VC的安裝路徑中找到WINMAIN.CPP文件,AfxWinMain函數(shù)就是VC編譯器的入口,去掉一些不重要的東西后得到如下得程序。
          int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)
          {
          int nReturnCode = -1;
          CWinApp* pApp = AfxGetApp(); ///獲取應(yīng)用程序類的指針與文檔視圖無關(guān)。
          AfxWinInit(hInstance, hPrevInstance,lpCmdLine,nCmdShow); ///與文檔視圖無關(guān)。
          pApp->InitApplication();  ///初始化應(yīng)用程序詳見下文。
          pApp->InitInstance();     ///最重要下面祥述
          nReturnCode = pApp->Run();///消息循環(huán)直到應(yīng)用程序被關(guān)閉。與文檔視圖無關(guān)。
          AfxWinTerm();      ///與文檔視圖無關(guān)。
          return nReturnCode;///整個應(yīng)用結(jié)束。
          }
          下面分節(jié)講述。
          第二章InitApplication()函數(shù)
          在文件Appcore.cpp文件中InitApplication如下:
          BOOL CWinApp::InitApplication()
          {
           if (CDocManager::pStaticDocManager != NULL)   ///這段和我們關(guān)系不大,暫時不理它
           {
            if (m_pDocManager == NULL)
             m_pDocManager = CDocManager::pStaticDocManager;
            CDocManager::pStaticDocManager = NULL;
           }

           if (m_pDocManager != NULL)
            m_pDocManager->AddDocTemplate(NULL);
           else
            CDocManager::bStaticInit = FALSE;  /*我們的程序?qū)?zhí)行到這句,表示文檔類用動態(tài)方式創(chuàng)建,也就是說用RUNTIME_CLASS來創(chuàng)建的(詳見下文)。*/
           return TRUE;
          }
            說明:CDocManager類是一個不公開的類,他主要用來管理多文檔模板對象的,對于單文檔只有一個文檔模板這個類不是很重要,但還是用它管理文檔模板的;本文只在相關(guān)之處作說明。bStaticInit是它的一個靜態(tài)成員。
          第三章InitInstance ()函數(shù)
            它一般被重載,在First工程中,InitInstance中和文檔視圖類有關(guān)的程序有下面的一些:

           CSingleDocTemplate* pDocTemplate;  ///定義指針
           pDocTemplate = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CFirstDoc),
            RUNTIME_CLASS(CMainFrame),
            RUNTIME_CLASS(CFirstView));    ///這條語句的作用見第一段
           AddDocTemplate(pDocTemplate);

           CCommandLineInfo cmdInfo;    ///定義一個對象
           ParseCommandLine(cmdInfo);    ///解析命令行并發(fā)送參數(shù),與文檔視圖無關(guān)

           if (!ProcessShellCommand(cmdInfo))   ///這是最重要的詳見的三段
            return FALSE;

           m_pMainWnd->ShowWindow(SW_SHOW);      ///顯示窗口
           m_pMainWnd->UpdateWindow();
          第一節(jié):new CSingleDocTemplate
          new CSingleDocTemplate其實就是創(chuàng)建一個CSingleDocTemplate對象并調(diào)用他的構(gòu)造函數(shù),要講清楚這句話,首先必須明白RUNTIME_CLASS結(jié)構(gòu),RUNTIME_CLASS結(jié)構(gòu)定義如下:
          #define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
          于是這句話展開后如下(關(guān)于符合##的具體意義參見MSDN):
           pDocTemplate = new CSingleDocTemplate(
          IDR_MAINFRAME,
          (CRuntimeClass*)(&CFirstDoc::classCFirstDoc),
          (CRuntimeClass*)(&CMainFrame::classCMainFrame),
          (CRuntimeClass*)(&CFirstView::classCFirstView));
          這時我們會發(fā)現(xiàn)CfirstDoc,CmainFrame,CfirstView各會多冒出一個靜態(tài)成員出來,它究竟在哪里呢?查看這三個類的定義我們會發(fā)現(xiàn)每個類定義都有一個宏DECLARE_DYNCREATE,在實現(xiàn)文件中有另外一個宏IMPLEMENT_DYNCREATE。下面把這些宏的展開情況列出如下:
          (1)文檔類
          DECLARE_DYNCREATE(CFirstDoc) 展開后為:
          public: 
           static const CRuntimeClass classCFirstDoc; ///這就是上面所說的那個靜態(tài)成員
           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+類名為名字的靜態(tài)變量。然后定義一個返回這個靜態(tài)變量的函數(shù)GetRuntimeClass并在實現(xiàn)文件中實現(xiàn)之。最后定義并實現(xiàn)一個創(chuàng)建對象的函數(shù)CreateObject。

          好了,我們現(xiàn)在就來看看CSingleDocTemplate對象的構(gòu)造函數(shù)做了些什么:下面是他的實現(xiàn)
          CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource,
           CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,
           CRuntimeClass* pViewClass)
            : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)
          {
           m_pOnlyDoc = NULL;
          }
          它主要是初始化他的父類CDocTemplate,CDocTemplate做了些什么?如下:
          {
          <1>把資源ID及文檔、框架、視圖的三個CRuntimeClass結(jié)構(gòu)的靜態(tài)成員的地址(也就是傳進來的四個參數(shù)) 保存了起來。
          <2>LoadTemplate();///為指定的文檔模板對象裝載資源
          }
          至此這個new語句總算講完了。
          第二節(jié):AddDocTemplate(pDocTemplate);
          下面是他的實現(xiàn)函數(shù)
          void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)
          {
           if (m_pDocManager == NULL)
          m_pDocManager = new CDocManager; /*創(chuàng)建一個文檔管理類,對于單文檔常常只有一個文檔模板用CDocManager類并不是很重要,我們可以忽略這句*/
           m_pDocManager->AddDocTemplate(pTemplate); /*把文檔模板指針保存在CDocManager實例中由CDocManager去管理他,我們只要知道有這個事就行了*/
          }
          第三節(jié):ProcessShellCommand(cmdInfo)
             這個函數(shù)發(fā)送的一個命令就是FileNew其他的暫時不管他,其實所有的文檔類,框架類,視圖類都是在這個函數(shù)中創(chuàng)建的,在第一段中已經(jīng)講到應(yīng)用程序類將上三個類的CRuntimeClass一個靜態(tài)成員地址保存起來了,關(guān)于CRuntimeClass結(jié)構(gòu)的具體功能參照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主要是派發(fā)命令消息,跟我們關(guān)系最大的消息是菜單命令I(lǐng)D_FILE_NEW,下面講述他的處理函數(shù)OnFileNew
          第二段OnFileNew();
           OnFileNew只是簡單的調(diào)用文檔管理對象的同名函數(shù),他的描述如下:
          void CWinApp::OnFileNew()
          {
           if (m_pDocManager != NULL)
            m_pDocManager->OnFileNew();///只是調(diào)用CDocManager::OnFileNew
          }
          文檔管理類的OnFileNew去掉一些沒用的東西后實現(xiàn)如下:
          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);/*其實是調(diào)用CSingleDocTemplate::OpenDocumentFile函數(shù)下面祥述這里,因為在CDocTemplate 類中這個函數(shù)是一個純虛函數(shù)*/
          }
          下面簡述pTemplate->OpenDocumentFile(NULL);這個函數(shù),
          這是最重要的一個函數(shù),他執(zhí)行了如下6個步驟:

          <1>pDocument = CreateNewDocument();///創(chuàng)建一個新文檔
             {
          CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();/*這是才真正的創(chuàng)建文檔對象,這里創(chuàng)建的文檔對象就是上面RUNTIME_CLASS(CFirstDoc),中的對象這是MFC開發(fā)人員想出來的一種運行時創(chuàng)建對象的方法,應(yīng)該說比較不錯的,不知道C++Builder的是怎樣構(gòu)造的。*/
            AddDocument(pDocument);  ///把創(chuàng)建的對象指針記錄下來,以后還要用的。
          }
          <2> pFrame = CreateNewFrame(pDocument, NULL);  /*根據(jù)新文檔指針創(chuàng)建新文檔框架,這個比較重要下一段描述*/
          <3>SetDefaultTitle(pDocument);  ///設(shè)置缺省標題,不重要的
          <4>pDocument->OnNewDocument()   ///清除老文檔內(nèi)容
          {
           DeleteContents();   ///刪除原來的內(nèi)容而不管是否存盤
           SetModifiedFlag(FALSE);   ///清除曾經(jīng)修改過的標記
          }
          <5>pThread->m_pMainWnd = pFrame;  ///框架窗口作為的指針作為主窗口的指針
          <.6>InitialUpdateFrame(pFrame, pDocument, bMakeVisible);   /*使框架中的視圖窗口接受到OnInitialUpdat的調(diào)用,并作原始更新。*/

          至此第一段的OnFileNew函數(shù)總算講完。下面講<2> CreateNewFrame函數(shù)
          CreateNewFrame是CDocTemplate的成員函數(shù)如下3個步驟:
          <2.1>CCreateContext context;    ///創(chuàng)建CcreateContext對象.也就是我們重載LoadFrame函數(shù)的那個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(); ///真正創(chuàng)建框架的對象
          <2.3>pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,  
             NULL, &context)   ///加載資源,這是最重要的,下面講述
          {
          <2.3.1> GetIconWndClass(dwDefaultStyle, nIDResource)   ///加載資源并注冊窗口
           {
            AfxFindResourceHandle
            ::LoadIcon  ///加載光標資源
            CREATESTRUCT cs;  ///創(chuàng)建一個CREATESTRUCT結(jié)構(gòu)實例
            PreCreateWindow(cs);   /*調(diào)用窗口預(yù)創(chuàng)建函數(shù),這個函數(shù)我們可以重載的
            根據(jù)cs調(diào)用 AfxReGISterWndClass注冊框架窗口*/
          }
          <2.3.2> Create(…)  ///創(chuàng)建框架窗口,后面祥述,此處暫時不講。
          <2.3.3> m_hMenuDefault = ::GetMenu(m_hWnd);  ///獲取并保存菜單句柄
          <2.3.4> LoadAccelTable(MAKEINTRESOURCE(nIDResource));  ///裝載加速鍵表
          }
            下面講Create函數(shù),如下4個步驟:
            <2.3.2.1>hMenu = ::LoadMenu(hInst, lpszMenuName);  ///加載菜單
          <2.3.2.2>CREATESTRUCT cs;  ///創(chuàng)建一個CREATESTRUCT結(jié)構(gòu)實例
          cs.lpCreateParams = lpParam;  ///(此時的lpParam就是<2.1>建的context對象指針)
          <2.3.2.3> PreCreateWindow(cs);  ///調(diào)用窗口預(yù)創(chuàng)建函數(shù),這個函數(shù)我們可以重載的,又調(diào)用一次
          <2.3.2.4>調(diào)用API函數(shù)::CreateWindowEx真正創(chuàng)建框架窗口,創(chuàng)建過程中會發(fā)出WM_CREATE和WM_NCCREATE等消息。
          下面講接收WM_CREATE消息的消息處理函數(shù)CMainFrame::OnCreate,其他消息不重要
          <2.3.2.3.1>CMainFrame::OnCreate最重要的是調(diào)用CFrameWnd::OnCreateHelper函數(shù)如下:
          {
          <1>CWnd::OnCreate(lpcs);  ///調(diào)用缺省的
          <2>調(diào)用可重載函數(shù)OnCreateClient(lpcs,pContext)函數(shù), 在OnCreateClient函數(shù)內(nèi)只是調(diào)用CreateView(pContext, AFX_IDW_PANE_FIRST)函數(shù);
          CreateView函數(shù)步驟如下:
          {
          <2.1> pView=pContext->m_pNewViewClass->CreateObject(); /*創(chuàng)建視圖對象*/
          <2.2>pView->Create(…);/*調(diào)用視圖對象的Create函數(shù)真正創(chuàng)建視圖,其更詳細的動作可以不理他,但要知道這里會發(fā)生很多事情(跟創(chuàng)建框架窗口類似的);其他一些不重要的動作可以不理他;*/
          }
          <3>PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);/*發(fā)一個WM_SETMESSAGESTRING消息初始化消息串*/
          <4>RecalcLayout();  ///重定控制欄等無關(guān)動作
          }
          <2.3.2.3.2> m_wndToolBar.CreateEx;   ///創(chuàng)建工具欄
          <2.3.2.3.3> LoadToolBar;  /停靠工具欄等無關(guān)動作
          至此WM_CREATE消息處理講完,同時第三段的ProcessShellCommand函數(shù)也結(jié)束了
           
          第四節(jié) :m_pMainWnd->ShowWindow(SW_SHOW); ///顯示窗口
          第五節(jié) :m_pMainWnd-> UpdateWindow (); ///重畫窗口
          至此第二節(jié)InitInstance ()函數(shù)講完,
          第四章pApp->Run函數(shù)
          Run函數(shù)處理消息循環(huán)直到程序推出,已經(jīng)有很多書籍講到他的過程,本文不描述他。
          第五章總結(jié)
          從上面的描述中可以看出,文檔/視圖結(jié)構(gòu)的關(guān)鍵在CRuntimeClass結(jié)構(gòu),通過CRuntimeClass結(jié)構(gòu)實現(xiàn)動態(tài)創(chuàng)建對象。

          posted on 2008-11-29 16:34 何克勤 閱讀(543) 評論(0)  編輯  收藏 所屬分類: VC++/MFC

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 敖汉旗| 古丈县| 白河县| 赣榆县| 三亚市| 甘孜| 临泉县| 康定县| 武定县| 临潭县| 离岛区| 宁城县| 彰武县| 乐陵市| 池州市| 台东市| 泾源县| 绥江县| 沐川县| 许昌市| 温州市| 左云县| 怀远县| 乳源| 峡江县| 泰来县| 砚山县| 建阳市| 万山特区| 祁阳县| 本溪| 普兰店市| 崇信县| 怀来县| 桃源县| 内江市| 阿合奇县| 通化县| 青冈县| 建德市| 山西省|