深圳劳资纠纷律师:(转)文档/视图结构中的各个部分是如何联系到一起的

来源:百度文库 编辑:偶看新闻 时间:2024/05/03 05:56:40

文档/视图结构是MFC中最有特色而又有难度的部分,在这当中涉及了应用、文档模板、文档、视图、MDI框架窗口、MDI子窗口等不同的对象,如果不了解 这些部分之间如何关联的话,就可能犯错误,也就很难编出有水平的文档/视图程序。比如我在初学VC编程的时候,为应用程序添加了两个文档模板,两个模板公 用一个文档类,只是视图不一样,期望当一个模板的文档的视图改变了文档后,调用UpdateAllViews后也能更新另一个文档模板的视图,结果当然是 不行的,原因就是对MFC的文档/视图结构没有深入的了解,了解的最好方法就是阅读一下MFC的源代码。下面就是我的笔记:

(一)应用程序对象与文档模板之间的联系:

        首先,在应用程序对象中有一个CDocManager指针类型的共有数据成员m_pDocManager,在CDocManager中维护一个 CPtrList类型的链表:m_tempateList,它是一个保护成员。InitInstance函数中调用CWinApp:: AddDocTemplate函数,实际上是调用m_pDocManager的AddDocTemplate函数向链表m_templateList添加 模板指针。CWinApp提供了GetFirstDocTemplatePosition和GetNextDocTemplate函数实现对 m_templateList链表的访问(实际上是调用了CDocManager的相关函数)。

         在文件操作方面CWinApp提供的最常用的功能是文件的新建(OnFileNew)和打开(OnFileOpen),它也是调用CDocManager 类的同名函数。对于新建,一般的时候在只有一个文档模板的时候,它新建一个空白的文件;如果有多个文档模板的时候,它会出现一个对话框提示选择文档类型。 它的源代码如下:

void CDocManager::OnFileNew()

{

       if (m_templateList.IsEmpty())

       {

                                .......

              return;

       }

                //取第一个文档模板的指针

       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

       }

                ......

                //参数为NULL的时候OpenDocument File会新建一个文件

       pTemplate->OpenDocumentFile(NULL);

}

打开文件:

void CDocManager::OnFileOpen()

{

       // 出现打开文件对话框

       CString newName;

       if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,

         OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))

              return; // open cancelled

       AfxGetApp()->OpenDocumentFile(newName);          //实际也是调用文档模板的同名函数

}

(二)文档模板与文档之间的联系:

        从上面看出应用程序对象对文件的新建和打开是依靠文档模板的OpenDocumentFile函数实现的。MFC的模板类是用来联系文档类、视类和框架类的,在它的构造函数就需要这三者的信息:

CDocTemplate ( UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass );

构造函数利用后三个参数为它的三个CruntimeClass*类型的保护成员赋值:

       m_pDocClass = pDocClass;

       m_pFrameClass = pFrameClass;

       m_pViewClass = pViewClass;

    文档模板分为单文档模板和多文档模板两种,这两个模板的实现是不同的,除了上面的三个成员,内部有彼此不相同的但是很重要的成员变量。对于多文档模板: CPtrList m_docList;,单文档模板:CDocument* m_pOnlyDoc;。它们都有一个成员函数AddDocument,分别各自的成员进行赋值操作,而在它们的父类的CDocTemplate中则是为 它所添加的文档的m_pDocTemplate变量赋值为模板自己的地址:

void CDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

       ASSERT(pDoc->m_pDocTemplate == NULL);  

       pDoc->m_pDocTemplate = this;

}

由于单文档模板只能拥有一个文档,所以它只是维护一个指向自己所拥有的模板的指针:m_pOnlyDoc,AddDocument函数就是要为这个成员赋值:

void CSingleDocTemplate::AddDocument(CDocument* pDoc)

{

                ......

       CDocTemplate::AddDocument(pDoc);

       m_pOnlyDoc = pDoc;

}
由于多文档模板可以拥有多个文档,所以它要维护的是包含它所打开的所有文档的指针的链表,所以它的AddDocument的实现为:

void CMultiDocTemplate::AddDocument(CDocument* pDoc)

{

                ......

       CDocTemplate::AddDocument(pDoc);

       m_docList..AddTail(pDoc);

}
    模板通过m_pOnlyDoc(单文档)或记住了自己所拥有的所有的模板的指针,并通过GetFirstDocPosition和GetNextDoc函 数可以实现对它所拥有的文档的访问,同时使文档记住了自己所属文档模板的指针,同时文档提供了GetDocTemplate()函数可以取得它所属的模 板。

对AddDocument函数的调用主要是发生在另一个成员函数CreateNewDocument里,它的作用是创建一个新的文档:

CDocument* CDocTemplate::CreateNewDocument()

{

       if (m_pDocClass == NULL)

       {

         ……

       }

       CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();

    ……

       AddDocument(pDocument);

       return pDocument;

}

CreateNewDocument函数主要利用文档类的运行时指针的函数CreateObject创建一个新文档对象,并利用AddDocument将其指针赋給相关的成员,留做以后使用。

    在应用程序的OnFileNew和OnFileOpen函数都使用了模板的OpenDocumentFile函数,而且在实际编程的时候也大都使用这个函 数。在MSDN的文档说这个函数当参数不为NULL的时候打开文件,否则就用上面所说的CreateNewDocument函数创建一个新文档,那么它是 如何实现的呢?

CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,

       BOOL bMakeVisible)

{

       CDocument* pDocument = NULL;

       CFrameWnd* pFrame = NULL;

       BOOL bCreated = FALSE;      // => doc and frame created

       BOOL bWasModified = FALSE;

    //如果已经有打开的文档,就会询问否保存文件

       if (m_pOnlyDoc != NULL)

       {

              pDocument = m_pOnlyDoc;

              if (!pDocument->SaveModified())

                     return NULL;     

              pFrame = (CFrameWnd*)AfxGetMainWnd();

                                ......

       }

    //创建新文件

       else

       {

              pDocument = CreateNewDocument();

              ASSERT(pFrame == NULL);    

              bCreated = TRUE;

       }

                ......

    //如果第一次创建文档则也要创建框架窗口。

       if (pFrame == NULL)

       {

              ASSERT(bCreated);

              // create frame - set as main document frame

              BOOL bAutoDelete = pDocument->m_bAutoDelete;

              pDocument->m_bAutoDelete = FALSE;

              pFrame = CreateNewFrame(pDocument, NULL);

              pDocument->m_bAutoDelete = bAutoDelete;

                                ......

       }

       if (lpszPathName == NULL)

       {

              // 为新文档设置默认标题

              SetDefaultTitle(pDocument);

        ……

      //一般的时候重载OnNewDocument初始化一些数据,如果返回FALSE,表示初始化失//败,销毁窗口。

              if (!pDocument->OnNewDocument())

              {

                                                ......

                     if (bCreated)

                            pFrame->DestroyWindow();    // will destroy document

                     return NULL;

              }

       }

       else

       {

              CWaitCursor wait;

              // open an existing document

              bWasModified = pDocument->IsModified();

              pDocument->SetModifiedFlag(FALSE);

              //OnOpenDocument函数重新初始化文档对象

              if (!pDocument->OnOpenDocument(lpszPathName))

              {

                     if (bCreated)

                     {

                //新建文档的情况

                            pFrame->DestroyWindow();   

                     }

                     else if (!pDocument->IsModified())

                     {

                            // 文档没有被修改,恢复原来文档的修改标志

                            pDocument->SetModifiedFlag(bWasModified);

                     }

                     else

                     {

                            // 修改了原始的文档

                            SetDefaultTitle(pDocument);

                            if (!pDocument->OnNewDocument())

                            {

                                   TRACE0("Error: OnNewDocument failed after trying to open a document - trying to continue.\n");

                            }

                     }

                     return NULL;        // open failed

              }

              pDocument->SetPathName(lpszPathName);

       }

       CWinThread* pThread = AfxGetThread();

       if (bCreated && pThread->m_pMainWnd == NULL)

       {

              pThread->m_pMainWnd = pFrame;

       }

       InitialUpdateFrame(pFrame, pDocument, bMakeVisible);

       return pDocument;

}

以下是多文档模板的OpenDocumentFile的实现

CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,

       BOOL bMakeVisible)

{

       //新建一个文档对象

       CDocument* pDocument = CreateNewDocument();

……

       BOOL bAutoDelete = pDocument->m_bAutoDelete;

       pDocument->m_bAutoDelete = FALSE;  

       CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);

       pDocument->m_bAutoDelete = bAutoDelete;

……

       if (lpszPathName == NULL)

    //当是新建的时候

       {

              SetDefaultTitle(pDocument);

              // avoid creating temporary compound file when starting up invisible

              if (!bMakeVisible)

                     pDocument->m_bEmbedded = TRUE;

              if (!pDocument->OnNewDocument())

              {

                     pFrame->DestroyWindow();

                     return NULL;

              }

              m_nUntitledCount++;

       }

       else

       {

              // 打开一个已经存在的文件

              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;

}

    从上面看出模板类的OpenDocumentFile函数里,利用CreateNewDocument对象使文档对象与模板对象建立了联系,利用了CreateNewFrame函数使框架窗口与文档、视图、模板发生了联系:


CreateNewFrame()跟踪

在新建SDI文档的过程中,框架指针为空(pFrame = NULL),需要根据新创建的文档指针pDocument来CreateNewFrame;在新建或打开MDI文档的过程中,则一直需要CreateNewFrame。

1       CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)

{

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

       ...

        CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);

       ...

}

2       CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{

       if (pDoc != NULL)

              ASSERT_VALID(pDoc);

       // create a frame wired to the specified document

 

       ASSERT(m_nIDResource != 0); // must have a resource ID to load from

       CCreateContext context;

       context.m_pCurrentFrame = pOther;

       context.m_pCurrentDoc = pDoc;

       context.m_pNewViewClass = m_pViewClass;

       context.m_pNewDocTemplate = this;

 

       if (m_pFrameClass == NULL)

       {

              TRACE0("Error: you must override CDocTemplate::CreateNewFrame./n");

              ASSERT(FALSE);

              return NULL;

       }

       CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();//利用CRunTimeClass真正创建框架指针

       if (pFrame == NULL)

       {

              TRACE1("Warning: Dynamic create of frame %hs failed./n",

                     m_pFrameClass->m_lpszClassName);

              return NULL;

       }

       ASSERT_KINDOF(CFrameWnd, pFrame);

 

       if (context.m_pNewViewClass == NULL)

              TRACE0("Warning: creating frame with no default view./n");

 

       // create new from resource

       if (!pFrame->LoadFrame(m_nIDResource,

                     WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles

                     NULL, &context))

       {

              TRACE0("Warning: CDocTemplate couldn't create a frame./n");

              // frame will be deleted in PostNcDestroy cleanup

              return NULL;

       }

}

3       BOOL CFrameWnd/CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,  CWnd* pParentWnd, CCreateContext* pContext)
{

       // only do this once

       ASSERT_VALID_IDR(nIDResource);

       ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);

 

       m_nIDHelp = nIDResource;    // ID for help context (+HID_BASE_RESOURCE)

 

       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);

       LPCTSTR lpszTitle = m_strTitle;

       if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault,

         pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))

       {

              return FALSE;   // will self destruct on failure normally

       }

 

       // save the default menu handle

       ASSERT(m_hWnd != NULL);

       m_hMenuDefault = ::GetMenu(m_hWnd);

 

       // load accelerator resource

       LoadAccelTable(MAKEINTRESOURCE(nIDResource));

 

       if (pContext == NULL)   // send initial update

              SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);//对于单文档,由于视图已经建立,发出WM_INITIALUPDATE消息,调用OnInitialUpdate()。

       return TRUE;

 

}

 

 BOOL CMDIChildWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
              CWnd* pParentWnd, CCreateContext* pContext)
{
       // only do this once
       ASSERT_VALID_IDR(nIDResource);
       ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);

       m_nIDHelp = nIDResource;    // ID for help context (+HID_BASE_RESOURCE)

       // parent must be MDI Frame (or NULL for default)
       ASSERT(pParentWnd == NULL || pParentWnd->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd)));
       // will be a child of MDIClient
       ASSERT(!(dwDefaultStyle & WS_POPUP));
       dwDefaultStyle |= WS_CHILD;

       // if available - get MDI child menus from doc template
       CMultiDocTemplate* pTemplate;
       if (pContext != NULL &&
              (pTemplate = (CMultiDocTemplate*)pContext->m_pNewDocTemplate) != NULL)
       {
              ASSERT_KINDOF(CMultiDocTemplate, pTemplate);
              // get shared menu from doc template
              m_hMenuShared = pTemplate->m_hMenuShared;
              m_hAccelTable = pTemplate->m_hAccelTable;
       }
       else
       {
              TRACE(traceAppMsg, 0, "Warning: no shared menu/acceltable for MDI Child window./n");
                     // if this happens, programmer must load these manually
       }

       CString strFullString, strTitle;
       if (strFullString.LoadString(nIDResource))
              AfxExtractSubString(strTitle, strFullString, 0);    // first sub-string

       ASSERT(m_hWnd == NULL);
       if (!Create(GetIconWndClass(dwDefaultStyle, nIDResource),
         strTitle, dwDefaultStyle, rectDefault,
         (CMDIFrameWnd*)pParentWnd, pContext))
       {
              return FALSE;   // will self destruct on failure normally
       }

       // it worked !
       return TRUE;
}

4.1       LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource)
{
       ASSERT_VALID_IDR(nIDResource);
       HINSTANCE hInst = AfxFindResourceHandle(
              ATL_MAKEINTRESOURCE(nIDResource), ATL_RT_GROUP_ICON);
       HICON hIcon = ::LoadIconW(hInst, ATL_MAKEINTRESOURCEW(nIDResource));
       if (hIcon != NULL)
       {
              CREATESTRUCT cs;
              memset(&cs, 0, sizeof(CREATESTRUCT));
              cs.style = dwDefaultStyle;
              PreCreateWindow(cs);//第一次调用
                     // will fill lpszClassName with default WNDCLASS name
                     // ignore instance handle from PreCreateWindow.

              WNDCLASS wndcls;
              if (cs.lpszClass != NULL &&
                     AfxCtxGetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndcls) &&
                     wndcls.hIcon != hIcon)
              {
                     // register a very similar WNDCLASS
                     return AfxRegisterWndClass(wndcls.style,
                            wndcls.hCursor, wndcls.hbrBackground, hIcon);
              }
       }
       return NULL;        // just use the default
}
4.1.2       用MFC的全局函数AfxRegisterWndClass注册WNDCLASS,不需要定义所有字段,只需要关注其4个参数值。函数原型:LPCTSTR AfxRegisterWndClass(UINT nClassStyle,HCURSOR hCursor=0,HBRUSH hbrBackground=0,HICON hIcon=0);

第一个参数指定类样式,定义窗口的某种操作特性;第二个参数指定窗口识别“类光标”;第三个参数指定窗口默认背景颜色,可以传递一个画刷句柄,也可以指定一个预定义的Windows系统颜色并加1,第四个参数指定windows用来在桌面上、任务栏和其它地方代表应用程序的图标句柄,可以自定义图标然后加载,也可以加载系统图标。

该函数返回一个包含WNDCLASS名称的非空结尾字符串的指针,作为CreateEX函数的参数,用以创建窗口。

如:

CString strWndClass=AfxRegisterWndClass(CS_DBLCLKS, AfxGetApp()->LoadStandardCursor(IDC_ARROW), (HBRUSH)(COLOR_3DFACE+1), AfxGetApp()->LoadStandardIcon(IDI_WINLOGO));

CreateEx(0,strWndClass,_T("my program"),
WS_OVERLAPPED|WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL);

 

4.2    BOOL CMDIChildWnd::Create(LPCTSTR lpszClassName,
       LPCTSTR lpszWindowName, DWORD dwStyle,
       const RECT& rect, CMDIFrameWnd* pParentWnd,
       CCreateContext* pContext)
{
       if (pParentWnd == NULL)
       {
              CWinThread *pThread = AfxGetThread();
              ENSURE_VALID(pThread);
              CWnd* pMainWnd = pThread->m_pMainWnd;
              ENSURE_VALID(pMainWnd);
              ASSERT_KINDOF(CMDIFrameWnd, pMainWnd);
              pParentWnd = (CMDIFrameWnd*)pMainWnd;
       }
       ASSERT(::IsWindow(pParentWnd->m_hWndMDIClient));

       // insure correct window positioning
       pParentWnd->RecalcLayout();

       // first copy into a CREATESTRUCT for PreCreate
       CREATESTRUCT cs;
       cs.dwExStyle = 0L;
       cs.lpszClass = lpszClassName;
       cs.lpszName = lpszWindowName;
       cs.style = dwStyle;
       cs.x = rect.left;
       cs.y = rect.top;
       cs.cx = rect.right - rect.left;
       cs.cy = rect.bottom - rect.top;
       cs.hwndParent = pParentWnd->m_hWnd;
       cs.hMenu = NULL;
       cs.hInstance = AfxGetInstanceHandle();
       cs.lpCreateParams = (LPVOID)pContext;

       if (!PreCreateWindow(cs))//CChildFrame第二次调用
       {
              PostNcDestroy();
              return FALSE;
       }
       // extended style must be zero for MDI Children (except under Win4)
       ASSERT(cs.hwndParent == pParentWnd->m_hWnd);    // must not change

       // now copy into a MDICREATESTRUCT for real create
       MDICREATESTRUCT mcs;
       mcs.szClass = cs.lpszClass;
       mcs.szTitle = cs.lpszName;
       mcs.hOwner = cs.hInstance;
       mcs.x = cs.x;
       mcs.y = cs.y;
       mcs.cx = cs.cx;
       mcs.cy = cs.cy;
       mcs.style = cs.style & ~(WS_MAXIMIZE | WS_VISIBLE);
       mcs.lParam = (LPARAM)cs.lpCreateParams;

       // create the window through the MDICLIENT window
       AfxHookWindowCreate(this);
       HWND hWnd = (HWND)::SendMessage(pParentWnd->m_hWndMDIClient,
              WM_MDICREATE, 0, (LPARAM)&mcs);//发出WM_MDICREATE(WM_NCCREATEHE WM_CREATE)消息,会调用CMDIChildWnd::OnNcCreate和CMDIChildWnd::OnCreate

       if (!AfxUnhookWindowCreate())
              PostNcDestroy();        // cleanup if MDICREATE fails too soon

       if (hWnd == NULL)
              return FALSE;

       // special handling of visibility (always created invisible)
       if (cs.style & WS_VISIBLE)//在前面PreCreateWindow中修改的cs属性必须有WS_VISIBLE,WS_MAXIMIZE才能生效的原因在此
       {
              // place the window on top in z-order before showing it
              ::BringWindowToTop(hWnd);

              // show it as specified
              if (cs.style & WS_MINIMIZE)
                     ShowWindow(SW_SHOWMINIMIZED);//如果可见,子框架窗口显示,在ActivateFrame中将再次ShowWindow
              else if (cs.style & WS_MAXIMIZE)
                     ShowWindow(SW_SHOWMAXIMIZED);
              else
                     ShowWindow(SW_SHOWNORMAL);

              // make sure it is active (visibility == activation)
              pParentWnd->MDIActivate(this);

              // refresh MDI Window menu
              ::SendMessage(pParentWnd->m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0);
       }

       ASSERT(hWnd == m_hWnd);
       return TRUE;
}
  

 

BOOL CFrameWnd/CMDIFrameWnd::Create(LPCTSTR lpszClassName,
       LPCTSTR lpszWindowName,
       DWORD dwStyle,
       const RECT& rect,
       CWnd* pParentWnd,
       LPCTSTR lpszMenuName,
       DWORD dwExStyle,
       CCreateContext* pContext)
{
       HMENU hMenu = NULL;
       if (lpszMenuName != NULL)
       {
              // load in a menu that will get destroyed when window gets destroyed
              HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);
              if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
              {
                     TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd./n");
                     PostNcDestroy();            // perhaps delete the C++ object
                     return FALSE;
              }
       }

       m_strTitle = lpszWindowName;    // save title for later

       if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
              rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
              pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
       {
              TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd./n");
              if (hMenu != NULL)
                     DestroyMenu(hMenu);
              return FALSE;
       }

       return TRUE;
}

5     BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
       LPCTSTR lpszWindowName, DWORD dwStyle,
       int x, int y, int nWidth, int nHeight,
       HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
       ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||
              AfxIsValidAtom(lpszClassName));
       ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));
      
       // allow modification of several common create parameters
       CREATESTRUCT cs;
       cs.dwExStyle = dwExStyle;
       cs.lpszClass = lpszClassName;
       cs.lpszName = lpszWindowName;
       cs.style = dwStyle;
       cs.x = x;
       cs.y = y;
       cs.cx = nWidth;
       cs.cy = nHeight;
       cs.hwndParent = hWndParent;
       cs.hMenu = nIDorHMenu;
       cs.hInstance = AfxGetInstanceHandle();
       cs.lpCreateParams = lpParam;

       if (!PreCreateWindow(cs))//主框架窗口的第二次调用

       {
              PostNcDestroy();
              return FALSE;
       }

       AfxHookWindowCreate(this);
       HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
                     cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
                     cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);//调用API函数::CreateWindowEx真正创建框架窗口,创建过程中会发出WM_CREATE和WM_NCCREATE等消息。接收WM_CREATE消息的消息处理函数是CMainFrame::OnCreate()。

#ifdef _DEBUG
       if (hWnd == NULL)
       {
              TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X/n",
                     GetLastError());
       }
#endif

       if (!AfxUnhookWindowCreate())
              PostNcDestroy();        // cleanup if CreateWindowEx fails too soon

       if (hWnd == NULL)
              return FALSE;
       ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
       return TRUE;
}

6      BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
       if( !CFrameWnd::PreCreateWindow(cs) )
              return FALSE;
       // TODO: 在此处通过修改

       //  CREATESTRUCT cs 来修改窗口类或样式

       return TRUE;
}

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
       if (cs.lpszClass == NULL)
       {
              VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
              cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
       }

       if (cs.style & FWS_ADDTOTITLE)
              cs.style |= FWS_PREFIXTITLE;

       cs.dwExStyle |= WS_EX_CLIENTEDGE;

       return TRUE;
}

 7     

BOOL CMDIChildWnd::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
{
       if (!CFrameWnd::OnNcCreate(lpCreateStruct))
              return FALSE;

       // handle extended styles under Win4
       // call PreCreateWindow again just to get dwExStyle
       VERIFY(PreCreateWindow(*lpCreateStruct));//CChildFrame第三次调用
       SetWindowLong(m_hWnd, GWL_EXSTYLE, lpCreateStruct->dwExStyle);

       return TRUE;
}

int CMDIChildWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
       // call base class with lParam context (not MDI one)
       MDICREATESTRUCT* lpmcs;
       lpmcs = (MDICREATESTRUCT*)lpCreateStruct->lpCreateParams;
       CCreateContext* pContext = (CCreateContext*)lpmcs->lParam;

       return OnCreateHelper(lpCreateStruct, pContext);//此函数调用的是CFrameWnd的OnCreateHelper,因此子框架窗口也将产生一个视图

}
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
       if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
              return -1;
      
       if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
              | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
              !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
       {
              TRACE0("未能创建工具栏/n");
              return -1;      // 未能创建
       }

       if (!m_wndStatusBar.Create(this) ||
              !m_wndStatusBar.SetIndicators(indicators,
                sizeof(indicators)/sizeof(UINT)))
       {
              TRACE0("未能创建状态栏/n");
              return -1;      // 未能创建
       }

       // TODO: 如果不需要可停靠工具栏,则删除这三行
       m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
       EnableDocking(CBRS_ALIGN_ANY);
       DockControlBar(&m_wndToolBar);

       return 0;
}

       int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
{
       ENSURE_ARG(lpcs != NULL);
       CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;
       return OnCreateHelper(lpcs, pContext);
}
8       int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
       if (CWnd::OnCreate(lpcs) == -1)
              return -1;

       // create special children first
       if (!OnCreateClient(lpcs, pContext))
       {
              TRACE(traceAppMsg, 0, "Failed to create client pane/view for frame./n");
              return -1;
       }

       // post message for initial message string
       PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);

       // make sure the child windows have been properly sized
       RecalcLayout();

       return 0;   // create ok
}

9      

BOOL CMDIFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext*)
{
       MDICREATESTRUCT* lpmcs;
       lpmcs = (MDICREATESTRUCT*)lpCreateStruct->lpCreateParams;
       CCreateContext* pContext = (CCreateContext*)lpmcs->lParam;

 

       return CreateClient(lpcs, pMenu);
}

       BOOL CMDIFrameWnd::CreateClient(LPCREATESTRUCT lpCreateStruct,
       CMenu* pWindowMenu)//对于主框架窗口MainFrm调用,则不会新建视图
{
       ASSERT(m_hWnd != NULL);
       ASSERT(m_hWndMDIClient == NULL);
       DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_BORDER |
              WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
              MDIS_ALLCHILDSTYLES;    // allow children to be created invisible

       //此处加入了WS_VISIBLE,因此对于MainFrm在PreCreateWindow中不需要加入该属性,至只要加入WS_MAXIMIZED就能实现最大化。而对于ChildFrm由于没有加入WS_VISIBLE,只有默认属性WS_OVERLAPPEDWINDOW|FWS_ADDTOTITILE,因此在PreCreateWindow中必须加入WS_VISIBLE和WS_MAXIMIZED,才能正确的最大化!
       DWORD dwExStyle = 0;
       // will be inset by the frame

       // special styles for 3d effect on Win4
       dwStyle &= ~WS_BORDER;
       dwExStyle = WS_EX_CLIENTEDGE;

       CLIENTCREATESTRUCT ccs;
       ccs.hWindowMenu = pWindowMenu->GetSafeHmenu();
              // set hWindowMenu for MFC V1 backward compatibility
              // for MFC V2, window menu will be set in OnMDIActivate
       ccs.idFirstChild = AFX_IDM_FIRST_MDICHILD;

       if (lpCreateStruct->style & (WS_HSCROLL|WS_VSCROLL))
       {
              // parent MDIFrame's scroll styles move to the MDICLIENT
              dwStyle |= (lpCreateStruct->style & (WS_HSCROLL|WS_VSCROLL));

              // fast way to turn off the scrollbar bits (without a resize)
              ModifyStyle(WS_HSCROLL|WS_VSCROLL, 0, SWP_NOREDRAW|SWP_FRAMECHANGED);
       }

       // Create MDICLIENT control with special IDC
       if ((m_hWndMDIClient = ::AfxCtxCreateWindowEx(dwExStyle, _T("mdiclient"), NULL,
              dwStyle, 0, 0, 0, 0, m_hWnd, (HMENU)AFX_IDW_PANE_FIRST,
              AfxGetInstanceHandle(), (LPVOID)&ccs)) == NULL)
       {
              TRACE(traceAppMsg, 0, _T("Warning: CMDIFrameWnd::OnCreateClient: failed to create MDICLIENT.")
                     _T(" GetLastError returns 0x%8.8X/n"), ::GetLastError());
              return FALSE;
       }
       // Move it to the top of z-order
       ::BringWindowToTop(m_hWndMDIClient);

       return TRUE;
}

 BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)//对于子框架窗口ChildFrm调用的是父类的,则产生一个新的视图
{
       // default create client will create a view if asked for it
       if (pContext != NULL && pContext->m_pNewViewClass != NULL)
       {
              if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
                     return FALSE;
       }
       return TRUE;
}
10       CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
{
       ASSERT(m_hWnd != NULL);
       ASSERT(::IsWindow(m_hWnd));
       ENSURE_ARG(pContext != NULL);
       ENSURE_ARG(pContext->m_pNewViewClass != NULL);

       // Note: can be a CWnd with PostNcDestroy self cleanup
       CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();//真正创建的视图对象
       if (pView == NULL)
       {
              TRACE(traceAppMsg, 0, "Warning: Dynamic create of view type %hs failed./n",
                     pContext->m_pNewViewClass->m_lpszClassName);
              return NULL;
       }
       ASSERT_KINDOF(CWnd, pView);

       // views are always created with a border!
       if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
              CRect(0,0,0,0), this, nID, pContext))
       {
              TRACE(traceAppMsg, 0, "Warning: could not create view for frame./n");
              return NULL;        // can't continue without a view
       }

       if (pView->GetExStyle() & WS_EX_CLIENTEDGE)
       {
              // remove the 3d style from the frame, since the view is
              //  providing it.
              // make sure to recalc the non-client area
              ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
       }
       return pView;
}

 

在完成创建后,就要调用IntitalUpdateFrame()来显示框架窗口和视图了,详见下节。