最近正在看《mfc深入浅出》,看到第8章Document/View深入探讨时候,对Document/View的建立过程不是很清楚,于是下大力气猛啃书和各种百度,明白了一些,做个小结:
先从CTest_DrawRectApp::InitInstance()开始
Test_DrawRect.cpp(这个文件是我建立名为Test_DrawRect工程自动生成的文件)
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTest_DrawRectDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CTest_DrawRectView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
afxwin.h
class CSingleDocTemplate : public CDocTemplate
class AFX_NOVTABLE CDocTemplate : public CCmdTarget
{
DECLARE_DYNAMIC(CDocTemplate)
......
virtual CDocument* CreateNewDocument();
virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther);
......
}
doctempl.cpp
CDocument* CDocTemplate::CreateNewDocument()
{
// default implementation constructs one from CRuntimeClass
if (m_pDocClass == NULL)
{
TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewDocument.\n");
ASSERT(FALSE);
return NULL;
}
CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
if (pDocument == NULL)
{
TRACE(traceAppMsg, 0, "Warning: Dynamic create of document type %hs failed.\n",
m_pDocClass->m_lpszClassName);
return NULL;
}
ASSERT_KINDOF(CDocument, pDocument);
AddDocument(pDocument);
return pDocument;
}
/
// Default frame creation
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)
{
TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewFrame.\n");
ASSERT(FALSE);
return NULL;
}
CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
if (pFrame == NULL)
{
TRACE(traceAppMsg, 0, "Warning: Dynamic create of frame %hs failed.\n",
m_pFrameClass->m_lpszClassName);
return NULL;
}
ASSERT_KINDOF(CFrameWnd, pFrame);
if (context.m_pNewViewClass == NULL)
TRACE(traceAppMsg, 0, "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))
{
TRACE(traceAppMsg, 0, "Warning: CDocTemplate couldn't create a frame.\n");
// frame will be deleted in PostNcDestroy cleanup
return NULL;
}
// it worked !
return pFrame;
}
上面的意思就是DocTemplate这个类管理CDocument、CFrameWnd、CView这三个对象,在CDocTemplate类里有创建CFrameWnd、CDocument这两个对象的函数。那么CView对象在哪里生成呢?
在CFrameWnd里生成。
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, 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;
}
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;
}
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;
}
int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
{
ENSURE_ARG(lpcs != NULL);
CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;
return OnCreateHelper(lpcs, pContext);
}
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
}
这段代码的意思是,在CSingleDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)中,使得CFrameWnd::Create()得到调用,CFrameWnd::Create(...)调用CFrameWnd::CreateEx(...),此函数会使得系统在还未创建窗口前发送一个WM_CREATE消息,此消息得到CFrameWnd::OnCreate(...)响应,CFrameWnd::OnCreate(...)调用CFrameWnd::OnCreateHelper(...)函数,CFrameWnd::OnCreateHelper(...)函数会调用CFrameWnd::OnCreateClient(...)函数,而CFrameWnd::OnCreateClient(...)函数会最终调用CFrameWnd::OnCreateView(...)完成CView对象的生成。
总结过程就是:
CXXXApp::InitInstance()->CSingleTemplate::CreateNewDocument(),CSingleTemplate::CreateNewFrame()->
CFrameWnd::Create()->CFrameWnd::CreateEx()->发送WM_CREATE消息->CFrameWnd::OnCreate()->
CFrameWnd::OnCreateHelper()->CFrameWnd::OnCreateClient()->CFrameWnd::CreateView()(可算到了!)。