MFC SDI应用程序的启动顺序

来源:互联网 发布:软件行业销售招聘 编辑:程序博客网 时间:2024/05/21 07:58
跟踪了MFC SDI应用程序的源代码,搞清了其启动顺序
初始化工作:
void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)
{
    
if (m_pDocManager == NULL)
        m_pDocManager 
= new CDocManager;
    m_pDocManager
->AddDocTemplate(pTemplate);
}


void CDocManager::AddDocTemplate(CDocTemplate* pTemplate)
{
    
if (pTemplate == NULL)
    
{
        
if (pStaticList != NULL)
        
{
            POSITION pos 
= pStaticList->GetHeadPosition();
            
while (pos != NULL)
            
{
                CDocTemplate
* pTemplate =
                    (CDocTemplate
*)pStaticList->GetNext(pos);
                AddDocTemplate(pTemplate);
            }

            delete pStaticList;
            pStaticList 
= NULL;
        }

        bStaticInit 
= FALSE;
    }

    
else
    
{
        ASSERT_VALID(pTemplate);
        ASSERT(m_templateList.Find(pTemplate, NULL) 
== NULL);// must not be in list
        pTemplate->LoadTemplate();
        m_templateList.AddTail(pTemplate);
    }

}


POSITION CPtrList::AddTail(
void* newElement)
{

    ASSERT_VALID(
this);

    CNode
* pNewNode = NewNode(m_pNodeTail, NULL);
    pNewNode
->data = newElement;
    
if (m_pNodeTail != NULL)
        m_pNodeTail
->pNext = pNewNode;
    
else
        m_pNodeHead 
= pNewNode;
    m_pNodeTail 
= pNewNode;
    
return (POSITION) pNewNode;

}


class CNewTypeDlg : public CDialog
{
protected:
    CPtrList
*   m_pList;        // actually a list of doc templates
public:
    CDocTemplate
*   m_pSelectedTemplate;

public:
    
//{{AFX_DATA(CNewTypeDlg)
    enum { IDD = AFX_IDD_NEWTYPEDLG };
    
//}}AFX_DATA
    CNewTypeDlg(CPtrList* pList) : CDialog(CNewTypeDlg::IDD)
    
{
        m_pList 
= pList;
        m_pSelectedTemplate 
= NULL;
    }

    
~CNewTypeDlg() { }

protected:
    BOOL OnInitDialog();
    
void OnOK();
    
//{{AFX_MSG(CNewTypeDlg)
    
//}}AFX_MSG
    DECLARE_MESSAGE_MAP()
}
;


POSITION CPtrList::Find(
void* searchValue, POSITION startAfter) const
{
    ASSERT_VALID(
this);

    CNode
* pNode = (CNode*) startAfter;
    
if (pNode == NULL)
    
{
        pNode 
= m_pNodeHead;  // start at head
    }

    
else
    
{
        ASSERT(AfxIsValidAddress(pNode, 
sizeof(CNode)));
        pNode 
= pNode->pNext;  // start after the one specified
    }

    
for (; pNode != NULL; pNode = pNode->pNext)
        
if (pNode->data == searchValue)
            
return (POSITION) pNode;
    
return NULL;
}


Step1:
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;
        
// If we've been asked to open a file, call OpenDocumentFile()
    case CCommandLineInfo::FileOpen:
        
if (!OpenDocumentFile(rCmdInfo.m_strFileName))
            bResult 
= FALSE;
        
break;
        
// If the user wanted to print, hide our main window and
        
// fire a message to ourselves to start the printing
    case CCommandLineInfo::FilePrintTo:
    
case CCommandLineInfo::FilePrint:
        m_nCmdShow 
= SW_HIDE;
        ASSERT(m_pCmdInfo 
== NULL);
        OpenDocumentFile(rCmdInfo.m_strFileName);
        m_pCmdInfo 
= &rCmdInfo;
        m_pMainWnd
->SendMessage(WM_COMMAND, ID_FILE_PRINT_DIRECT);
        m_pCmdInfo 
= NULL;
        bResult 
= FALSE;
        
break;
        
// If we're doing DDE, hide ourselves
    case CCommandLineInfo::FileDDE:
        m_pCmdInfo 
= (CCommandLineInfo*)m_nCmdShow;
        m_nCmdShow 
= SW_HIDE;
        
break;
    
// If we've been asked to unregister, unregister and then terminate
    case CCommandLineInfo::AppUnregister:
        
{
            UnregisterShellFileTypes();
            BOOL bUnregistered 
= Unregister();
            
// if you specify /EMBEDDED, we won't make an success/failure box
            
// this use of /EMBEDDED is not related to OLE
            if (!rCmdInfo.m_bRunEmbedded)
            
{
                
if (bUnregistered)
                    AfxMessageBox(AFX_IDP_UNREG_DONE);
                
else
                    AfxMessageBox(AFX_IDP_UNREG_FAILURE);
            }

            bResult 
= FALSE;    // that's all we do
            
// If nobody is using it already, we can use it.
            
// We'll flag that we're unregistering and not save our state
            
// on the way out. This new object gets deleted by the
            
// app object destructor.
            if (m_pCmdInfo == NULL)
            
{
                m_pCmdInfo 
= new CCommandLineInfo;
            m_pCmdInfo
->m_nShellCommand= CCommandLineInfo::AppUnregister;
            }

        }

        
break;
    }

    
return bResult;
}


Step2:
void CWinApp::OnFileNew()
{
    
if (m_pDocManager != NULL)
        m_pDocManager
->OnFileNew();
}
//m_pDocManager是CWinApp类的CDocManager类型的成员函数,管理应用程序的文档模板列表

Step3:
void CDocManager::OnFileNew()
{
    
if (m_templateList.IsEmpty())
    
{
        TRACE0(
"Error: no document templates registered with CWinApp. ");
        AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
        
return;
    }

    CDocTemplate
* pTemplate = (CDocTemplate*)m_templateList.GetHead();
    
if (m_templateList.GetCount() > 1)
    
{
        
// more than one document template to choose from
        
// bring up dialog prompting user
        CNewTypeDlg dlg(&m_templateList);
        
int nID = dlg.DoModal();
        
if (nID == IDOK)
            pTemplate 
= dlg.m_pSelectedTemplate;
        
else
            
return;     // none - cancel operation
    }

    ASSERT(pTemplate 
!= NULL);
    ASSERT_KINDOF(CDocTemplate, pTemplate);
    pTemplate
->OpenDocumentFile(NULL);
        
// if returns NULL, the user has already been alerted
}


Step4:
CDocument
* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
    BOOL bMakeVisible)
    
// if lpszPathName == NULL => create new file of this type
{
    CDocument
* pDocument = NULL;
    CFrameWnd
* pFrame = NULL;
    BOOL bCreated 
= FALSE;      // => doc and frame created
    BOOL bWasModified = FALSE;
    
if (m_pOnlyDoc != NULL)
    
{
        
// already have a document - reinit it
        pDocument = m_pOnlyDoc;
        
if (!pDocument->SaveModified())
            
return NULL;        // leave the original one
        pFrame = (CFrameWnd*)AfxGetMainWnd();
        ASSERT(pFrame 
!= NULL);
        ASSERT_KINDOF(CFrameWnd, pFrame);
        ASSERT_VALID(pFrame);
    }

    
else
    
{
        
// create a new document
        pDocument= CreateNewDocument();
        ASSERT(pFrame 
== NULL);     // will be created below
        bCreated = TRUE;
    }

    
if (pDocument == NULL)
    
{
        AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
        
return NULL;
    }

    ASSERT(pDocument 
== m_pOnlyDoc);
    
if (pFrame == NULL)
    
{
        ASSERT(bCreated);
        
// create frame - set as main document frame
        BOOL bAutoDelete = pDocument->m_bAutoDelete;
        pDocument
->m_bAutoDelete = FALSE;
                    
// don't destroy if something goes wrong
        pFrame=CreateNewFrame(pDocument,NULL);
pDocument
->m_bAutoDelete= bAutoDelete;
        
if (pFrame == NULL)
        
{
            AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
            delete pDocument;       
// explicit delete on error
            return NULL;
        }

    }

    
if (lpszPathName == NULL)
    
{
        
// create a new document
        SetDefaultTitle(pDocument);
        
// avoid creating temporary compound file when starting up invisible
        if (!bMakeVisible)
            pDocument
->m_bEmbedded = TRUE;
        
if (!pDocument->OnNewDocument())
        
{
            
// user has been alerted to what failed in OnNewDocument
            TRACE0("CDocument::OnNewDocument returned FALSE. ");
            
if (bCreated)
                pFrame
->DestroyWindow();    // will destroy document
            return NULL;
        }

    }

    
else
    
{
        CWaitCursor wait;
        
// open an existing document
        bWasModified = pDocument->IsModified();
        pDocument
->SetModifiedFlag(FALSE);  // not dirty for open
        if (!pDocument->OnOpenDocument(lpszPathName))
        
{
            
// user has been alerted to what failed in OnOpenDocument
            TRACE0("CDocument::OnOpenDocument returned FALSE. ");
            
if (bCreated)
            
{
                pFrame
->DestroyWindow();    // will destroy document
            }

            
else if (!pDocument->IsModified())
            
{
                
// original document is untouched
                pDocument->SetModifiedFlag(bWasModified);
            }

            
else
            
{
                
// we corrupted the original document
                SetDefaultTitle(pDocument);
                
if (!pDocument->OnNewDocument())
                
{
                    TRACE0(
"Error: OnNewDocument failed after trying to open a document - trying to continue. ");
                    
// assume we can continue
                }

            }

            
return NULL;        // open failed
        }

        pDocument
->SetPathName(lpszPathName);
    }

    CWinThread
* pThread = AfxGetThread();
    
if (bCreated && pThread->m_pMainWnd == NULL)
    
{
        
// set as main frame (InitialUpdateFrame will show the window)
        pThread->m_pMainWnd = pFrame;
    }

    InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
    
return pDocument;
}
//OpemDocumentFile是CDocTemplate类的虚函数

Step5:
第一步:创建文档对象
CDocument
* CDocTemplate::CreateNewDocument()
{
    
// default implementation constructs one from CRuntimeClass
    if (m_pDocClass == NULL)
    
{
        TRACE0(
"Error: you must override CDocTemplate::CreateNewDocument. ");
        ASSERT(FALSE);
        
return NULL;
    }

    CDocument
* pDocument = (CDocument*)m_pDocClass->CreateObject();
    
if (pDocument == NULL)
    
{
        TRACE1(
"Warning: Dynamic create of document type %hs failed. ",
            m_pDocClass
->m_lpszClassName);
        
return NULL;
    }

    ASSERT_KINDOF(CDocument, pDocument);
    AddDocument(pDocument);
    
return pDocument;
}

第二步:创建边框窗口
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. ");
        ASSERT(FALSE);
        
return NULL;
    }

    CFrameWnd
* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
    
if (pFrame == NULL)
    
{
        TRACE1(
"Warning: Dynamic create of frame %hs failed. ",
            m_pFrameClass
->m_lpszClassName);
        
return NULL;
    }

    ASSERT_KINDOF(CFrameWnd, pFrame);
    
if (context.m_pNewViewClass == NULL)
        TRACE0(
"Warning: creating frame with no default view. ");
    
// 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. ");
        
// frame will be deleted in PostNcDestroy cleanup
        return NULL;
    }

    
// it worked !
    return pFrame;
}


Step6:
BOOL CFrameWnd::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, 00, TRUE, TRUE);
    
return TRUE;
}
//LoadFrame注册了两个“窗口类”,一个为边框窗口,一个为视窗口,LoadFrame调用了CFrameWnd类的Create函数

Step7:
BOOL CFrameWnd::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, RT_MENU);
        
if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
        
{
            TRACE0(
"Warning: failed to load menu for CFrameWnd. ");
            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))
    
{
        TRACE0(
"Warning: failed to create CFrameWnd. ");
        
if (hMenu != NULL)
            DestroyMenu(hMenu);
        
return FALSE;
    }

    
return TRUE;
}


Step8:
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)
{
    
// 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); 
    (注: 此函数调用函数::SetWindowsHookEx,SetWindowsHookEx安装了一个WH_CBT类型的钩子,在调用CreateWindowEx时(在CreateWindowEx返回之前)窗口会发送WM_CREATE、 WM_NCCREATE等消息,钩子过程CBTProc会在窗口消息WM_CREATE、 WM_NCCREATE等发送前被调用,并提前得到窗口的句柄值。钩子过程CBTProc的任务是把窗口句柄赋给窗口对象(CWnd::m_hWnd),并调用SetWindowLong把窗口过程替换成AfxWndProc(如是控件还要保留原窗口过程,用CallWindowProc进行默认处理)。)                            
    HWND hWnd 
= ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
            cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
            cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
#ifdef _DEBUG
    
if (hWnd == NULL)
    
{
        TRACE1(
"Warning: Window creation failed: GetLastError returns 0x%8.8X ",
            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;
}

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) && afxData.bWin4)
        cs.style 
|= FWS_PREFIXTITLE;
    
if (afxData.bWin4)
        cs.dwExStyle 
|= WS_EX_CLIENTEDGE;
    
return TRUE;}
//发送WM_CREATE消息,从而创建视对象和视窗口

Step9:
int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
{
    CCreateContext
* pContext = (CCreateContext*)lpcs->lpCreateParams;
    
return OnCreateHelper(lpcs, pContext); 
}


Step10:
int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
    
if (CWnd::OnCreate(lpcs) == -1)
        
return -1;
    
// create special children first
    if (!OnCreateClient(lpcs, pContext))
    
{
        TRACE0(
"Failed to create client pane/view for frame. ");
        
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
}


Step11:
BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext
* pContext)
{
    
// 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;
}


Step12:
CWnd
* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
{
    ASSERT(m_hWnd 
!= NULL);
    ASSERT(::IsWindow(m_hWnd));
    ASSERT(pContext 
!= NULL);
    ASSERT(pContext
->m_pNewViewClass != NULL);
    
// Note: can be a CWnd with PostNcDestroy self cleanup
    CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
    
if (pView == NULL)
    
{
        TRACE1(
"Warning: Dynamic create of view type %hs failed. ",
            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))
    
{
        TRACE0(
"Warning: could not create view for frame. ");
        
return NULL;        // can't continue without a view
    }

    
if (afxData.bWin4 && (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;
}