MFC技术内幕系列之(二)---MFC文档视图结构内幕 上
来源:互联网 发布:淘宝卖家怎么创建套餐 编辑:程序博客网 时间:2024/05/21 09:44
引言:侯捷老师的"深入浅出MFC"一书的第8章中有“"Document/View"是MFC的基石。”一说,可以看出文档视图结构在MFC Framework中的地位是多么的重要。本文将以一个标准MFC应用程序向导作成的MDI程序为例,来和大家一起详细挖掘文档视图结构的内幕。
正文: ///////////////////////////////////////////// /* 1.回顾"InitInstance函数" */ ///////////////////////////////////////////// 在我的《MFC应用程序“生死因果”内幕》一文中,当谈到CMyWinApp::InitInstance()时,我只是粗略的讲了介绍了一下各个函数的功能,而忽略了很多细节,这里让我们在回顾一下CMyWinApp::InitInstance()函数,并将里面与文档视图结构有关的代码深入探讨一下: BOOL CMyApp::InitInstance()//只列出了与文档视图结构相关的源代码{//...
//文档模板将用作文档、框架窗口和视图之间的连接CMultiDocTemplate* pDocTemplate;pDocTemplate = new CMultiDocTemplate(IDR_MyTYPE, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CMyView));AddDocTemplate(pDocTemplate);// 创建主 MDI 框架窗口CMainFrame* pMainFrame = new CMainFrame;if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;m_pMainWnd = pMainFrame;// 仅当具有后缀时才调用 DragAcceptFiles// 在 MDI 应用程序中,这应在设置 m_pMainWnd 之后立即发生// 分析标准外壳命令、DDE、打开文件操作的命令行CCommandLineInfo cmdInfo;ParseCommandLine(cmdInfo);// 调度在命令行中指定的命令。如果// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。if (!ProcessShellCommand(cmdInfo)) return FALSE;// 主窗口已初始化,因此显示它并对其进行更新pMainFrame->ShowWindow(m_nCmdShow);pMainFrame->UpdateWindow();return TRUE;}
//////////////////////////////////////////// /* 2.初始化文档模板 */ ////////////////////////////////////////////分析以下代码: CMultiDocTemplate* pDocTemplate;pDocTemplate = new CMultiDocTemplate(IDR_MyTYPE, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CMyView)); 应用程序首先实例化一个CMultiDocTemplate对象,此过程也是对CMultiDocTemplate类数据成员的初始化过程。按调用次序我列出了以下源代码: 注释1: CDocTemplate构造函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/doctempl.cppCDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)//部分源代码{ASSERT_VALID_IDR(nIDResource);ASSERT(pDocClass == NULL || pDocClass->IsDerivedFrom(RUNTIME_CLASS(CDocument)));ASSERT(pFrameClass == NULL || pFrameClass->IsDerivedFrom(RUNTIME_CLASS(CFrameWnd)));ASSERT(pViewClass == NULL || pViewClass->IsDerivedFrom(RUNTIME_CLASS(CView)));
m_nIDResource = nIDResource;...// m_pDocClass = pDocClass;m_pFrameClass = pFrameClass;m_pViewClass = pViewClass;m_pOleFrameClass = NULL;m_pOleViewClass = NULL; ...//}以上为CMultiDocTemplate类的基类CDocTemplate构造函数的部分源代码,该函数初始化了四个重要的成员m_nIDResource,m_pDocClass,m_pFrameClass和m_pViewClass。 注释2: CMultiDocTemplate构造函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/docmulti.cpp CMultiDocTemplate::CMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass) : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass){ASSERT(m_docList.IsEmpty());
m_hMenuShared = NULL;m_hAccelTable = NULL;m_nUntitledCount = 0; // start at 1
// load resources in constructor if not statically allocatedif (!CDocManager::bStaticInit) LoadTemplate();} 看完以上代码后,来回过头看一看InitInstance函数将什么参数值传给了CMultiDocTemplate的构造函数。原来是一些RUNTIME_CLASS宏。以下是RUNTIME_CLASS宏的定义: 注释3: 以下的宏定义在:../Visual Studio.NET/vc7/atlmfc/include/afx.h中 #define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name) #define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name)) 这个地方是个难点,这将涉及到MFC的另一个重要技术---"执行期类型识别"。此项技术我将在MFC技术内幕系列之(三)---《MFC执行期类型识别与动态创建技术内幕》中详细讲解。回到眼前来,源代码中这样作是为了将CMyDoc,CChildFrame,CMyView各类中的static CRuntimeClass class##class_name地址赋予CMultiDocTemplate类的各CRuntimeClass*指针成员m_pDocClass,m_pFrameClass和m_pViewClass,这位以后的动态创建Document/Frame/View"三口组"打下了基础。 /////////////////////////////////////////// /* 3.文档模板列队 */ /////////////////////////////////////////// 文档模板初始化结束后,InitInstance函数调用了CWinApp::AddDocTemplate(pDocTemplate)函数,其主要目的是将以初始化后的那个文档模板加入到文档模板链表中,并由CDocManager类对象进行管理。以下操作就是为了完成此工作。
注释1: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/appui2.cppvoid CWinApp::AddDocTemplate(CDocTemplate* pTemplate)//将CMultiDocTemplate* pDocTemplate{ //传给pTemplateif (m_pDocManager == NULL) m_pDocManager = new CDocManager;m_pDocManager->AddDocTemplate(pTemplate);}注释2: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/docmgr.cppCDocManager::CDocManager(){}//目前是一个空函数;
void CDocManager::AddDocTemplate(CDocTemplate* pTemplate)//部分源代码{if (pTemplate == NULL){ ...//}else{ ASSERT_VALID(pTemplate); ASSERT(m_templateList.Find(pTemplate, NULL) == NULL);// must not be in list pTemplate->LoadTemplate(); m_templateList.AddTail(pTemplate);//CPtrList m_templateList is a member //of CDocManager}}
/////////////////////////////////////////////// /* 4.创建程序主框架窗口 */ /////////////////////////////////////////////// 应用程序实例化了一个CMainFrame类对象,并调用LoadFrame函数加载窗口资源创建主框架窗口。以下是创建主框架窗口的流程。 创建窗口的主要代码是:pMainFrame->LoadFrame(IDR_MAINFRAME);LoadFrame函数是MFC包装了窗口创建过程的函数,在后面动态创建Child窗口时,它还将披挂上阵(但稍有不同)。下面是它的源代码,让我们仔细分析一下:注释1: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/winmdi.cpp BOOL CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,CWnd* pParentWnd, CCreateContext* pContext) {if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext)) return FALSE;
// save menu to use when no active MDI child window is presentASSERT(m_hWnd != NULL);m_hMenuDefault = ::GetMenu(m_hWnd);if (m_hMenuDefault == NULL) TRACE(traceAppMsg, 0, "Warning: CMDIFrameWnd without a default menu./n");return TRUE;}CMDIFrameWnd::LoadFrame调用了其基类CFrameWnd的LoadFrame,并将参数原封不动的传给它。
注释2: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/winfrm.cppBOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,CWnd* pParentWnd, CCreateContext* pContext) //部分源代码{...//CString strFullString;if (strFullString.LoadString(nIDResource)) AfxExtractSubString(m_strTitle, strFullString, 0); // first sub-string
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
// attempt to create the windowLPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);CString strTitle = m_strTitle;if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)){ return FALSE; // will self destruct on failure normally}
...//if (pContext == NULL) // send initial update SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
return TRUE;} ////////////////////////////////////////////////////// /* 4.1注册应用程序主框架窗口类 */ ////////////////////////////////////////////////////// 在传统的Win32API编程中,创建窗口一般步骤是定义窗口类,注册窗口类,并调用::CreateWindow函数来创建。前面说过LoadFrame函数封装了MFC创建窗口的过程,那么也就是说LoadFrame函数将负责定义窗口类,注册窗口类等琐碎工作。下面我们就通过挖掘源代码来看看LoadFrame函数是如何完成这些工作的。 LoadFrame首先调用AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG), 注释1: 以下宏定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/afximpl.h #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) 注释2: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/wincore.cppBOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)//部分源代码{// mask off all classes that are already registeredAFX_MODULE_STATE* pModuleState = AfxGetModuleState();fToRegister &= ~pModuleState->m_fRegisteredClasses;if (fToRegister == 0) return TRUE;
LONG fRegisteredClasses = 0;
// common initializationWNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaultswndcls.lpfnWndProc = DefWindowProc;wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = afxData.hcurArrow;
INITCOMMONCONTROLSEX init;init.dwSize = sizeof(init);
// work to register classes as specified by fToRegister, populate fRegisteredClasses as we goif (fToRegister & AFX_WND_REG){ // Child windows - no brush, no icon, safest default class styles wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.lpszClassName = _afxWnd; if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WND_REG;}...//if (fToRegister & AFX_WNDMDIFRAME_REG){ // MDI Frame window (also used for splitter window) wndcls.style = CS_DBLCLKS; wndcls.hbrBackground = NULL; if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME)) fRegisteredClasses |= AFX_WNDMDIFRAME_REG;}if (fToRegister & AFX_WNDFRAMEORVIEW_REG){ // SDI Frame or MDI Child windows or views - normal colors wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)) fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;} ...//// must have registered at least as mamy classes as requestedreturn (fToRegister & fRegisteredClasses) == fToRegister; } MFC预定义了若干个“窗口类模板”,比如"AFX_WNDMDIFRAME_REG","AFX_WNDFRAMEORVIEW_REG"等,MFC在LoadFrame函数中调用AfxEndDeferRegisterClass函数为你的应用程序预注册了适当的窗口类。本例中预注册的窗口类为AFX_WNDFRAMEORVIEW_REG。(注意是预注册,如果你在后面更改了CREATESTRUCT结构的域成员,MFC还会根据你的更改重新为你的应用程序正式注册新的窗口类,稍候会有详细叙述) 预注册完窗口类,MFC将判断你是否想更改窗口类的各参数。若你更改了,则MFC会重新注册新类;否则源预注册的窗口类就将成为正式的窗口类。下面我们来看看MFC的判断过程:此判断过程由GetIconWndClass开始LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);注释3: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/winfrm.cppLPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource)//部分源代码{...//HICON hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(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 && GetClassInfo(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} GetIconWndClass函数将调用CMainFrame::PreCreateWindow(CREATESTRUCT& cs)来看看应用程序是否修改了CREATESTRUCT结构的域成员。CMainFrame::PreCreateWindow调用 CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs),后者的代码如下:BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs)//in winmdi.cpp{if (cs.lpszClass == NULL){ VERIFY(AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG)); cs.lpszClass = _afxWndMDIFrame;}return TRUE;}MFC将为应用程序注册AFX_WNDMDIFRAME_REG预定义窗口类,并设置cs.lpszClass = _afxWndMDIFrame。在应用程序的代码中我更改了cs结构:BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs){if( !CMDIFrameWnd::PreCreateWindow(cs) ) return FALSE;// TODO: 在此处通过修改 CREATESTRUCT cs 来修改窗口类或// 样式cs.dwExStyle=~WS_EX_CLIENTEDGE;return TRUE;} CMainFrame::PreCreateWindow返回后,GetIconWndClass函数调用GetClassInfo函数重新收集cs信息(此时的信息已是更改后的了),并调用AfxRegisterWndClass函数重新注册该窗口类(此窗口类为该应用程序的正式窗口类)。到此为止窗口类注册完毕,以后程序还会调用一次CMainFrame::PreCreateWindow,不过那此只是"过门不如"而已。 ///////////////////////////////////////////////// /* 4.2主框架窗口创建开始 */ /////////////////////////////////////////////////
开始进入创建框架窗口的实质阶段。看LoadFrame函数做了什么?原来它调用了Create函数。注释1: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/winfrm.cpp 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) { 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;}
简单地说CFrameWnd::Create函数调用了基类的CWnd::CreateEx;注释2: 以下函数定义在:../Visual Studio.NET/vc7/atlmfc/src/mfc/wincore.cppBOOL 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 parametersCREATESTRUCT 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 = ::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); ...// return TRUE;}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs){if( !CMDIFrameWnd::PreCreateWindow(cs) ) return FALSE;// TODO: 在此处通过修改 CREATESTRUCT cs 来修改窗口类或// 样式 cs.dwExStyle=~WS_EX_CLIENTEDGE;return TRUE;}
BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs){if (cs.lpszClass == NULL){ VERIFY(AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG)); cs.lpszClass = _afxWndMDIFrame;}return TRUE;} CWnd::CreateEx调用了CMainFrame::PreCreateWindow,但此次AfxDeferRegisterClass将不会被调用。也就是我上面所说的“过门不入”。
CWnd::CreateEx函数还调用了AfxHookWindowCreate(this);后者是干什么的呢?其实它与消息映射和命令传递有关,我将在MFC技术内幕系列之(四)--《MFC消息映射与消息传递内幕》一文中详解。 CWnd::CreateEx调用Win32API ::CreateWindowEx函数(传统的Win32API程序员一定不陌生这个函数),就这样主框架窗口创建结束。
////////////////////////////////////////////// /* 5.标准外壳命令解析 */ /////////////////////////////////////////////// MFC向导制作的标准MDI应用程序启动时,应用程序会自动启动一个子窗口框架(实际上是一套文档模板),这是为何呢?下面我将详细讲解一下这个创建过程. 其实这一过程也是在CMyWinApp::InitInstance()函数中完成的,看看下面代码:
CCommandLineInfo cmdInfo;ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE; 函数首先实例化一个CCommandLineInfo类对象cmdInfo,让我们看看CCommandLineInfo是个什么东东? //in afxwin.hclass CCommandLineInfo : public CObject//部分源代码{public:// Sets default valuesCCommandLineInfo(); ...//BOOL m_bShowSplash;BOOL m_bRunEmbedded;BOOL m_bRunAutomated;enum { FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE, AppRegister, AppUnregister, FileNothing = -1 } m_nShellCommand;
// not valid for FileNewCString m_strFileName;
// valid only for FilePrintToCString m_strPrinterName;CString m_strDriverName;CString m_strPortName;
~CCommandLineInfo(); // Implementation ...//};
再让我们来看看它的构造函数的实现:
//in appcore.cppCCommandLineInfo::CCommandLineInfo(){m_bShowSplash = TRUE;m_bRunEmbedded = FALSE;m_bRunAutomated = FALSE;m_nShellCommand = FileNew;}m_nShellCommand = FileNew;这一句对我们最重要;至于CWinApp::ParseCommandLine我想用MFC文档中的一句话来解释: Call this member function to parse the command line and send the parameters, one at a time, to CCommandLineInfo::ParseParam. 下面我们来看看外壳命令解析的主角:CWinApp::ProcessShellCommand//in appui2.cpp //DDE and ShellExecute support
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; case CCommandLineInfo::FilePrintTo:case CCommandLineInfo::FilePrint: ...// case CCommandLineInfo::FileDDE: ...//case CCommandLineInfo::AppRegister: ...// case CCommandLineInfo::AppUnregister: ...// }return bResult;} 挖掘源代码的确是了解MFC运行内幕的最好手段,大家一看源代码便知道如之何了。CCommandLineInfo构造函数中m_nShellCommand = FileNew;所以在ProcessShellCommand中对应的代码自然就一目了然了:CWinApp::OnFileNew()被调用了。
////////////////////////////////////////////////// /* 6.一套文档/视图即将诞生 */ ////////////////////////////////////////////////// 上文说CWinApp::OnFileNew()被调用了,那么就让我来看看其代码吧!//in appdlg.cppvoid CWinApp::OnFileNew(){ if (m_pDocManager != NULL)m_pDocManager->OnFileNew();}//in docmgr.cppvoid CDocManager::OnFileNew()//部分源代码{...// 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_PTR 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}//in docmulti.cppCDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)//部分源代码{CDocument* pDocument = CreateNewDocument();...// BOOL bAutoDelete = pDocument->m_bAutoDelete;pDocument->m_bAutoDelete = FALSE; // don't destroy if something goes wrongCFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);pDocument->m_bAutoDelete = bAutoDelete;...//
if (lpszPathName == NULL){ // create a new document - with default document name SetDefaultTitle(pDocument);
// avoid creating temporary compound file when starting up invisible if (!bMakeVisible) pDocument->m_bEmbedded = TRUE;
if (!pDocument->OnNewDocument()) { // user has be alerted to what failed in OnNewDocument TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE./n"); pFrame->DestroyWindow(); return NULL; }
// it worked, now bump untitled count m_nUntitledCount++;}else{ // open an existing document CWaitCursor wait; if (!pDocument->OnOpenDocument(lpszPathName)) { // user has be alerted to what failed in OnOpenDocument TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE./n"); pFrame->DestroyWindow(); return NULL; } pDocument->SetPathName(lpszPathName);}
InitialUpdateFrame(pFrame, pDocument, bMakeVisible);return pDocument;} ////////////////////////////////////////////// /* 6.1.子文档动态生成 */ ////////////////////////////////////////////// CMultiDocTemplate::OpenDocumentFile调用了CreateNewDocument(),这就是子文档动态生成的主函数。//in doctempl.cppCDocument* CDocTemplate::CreateNewDocument()//部分源代码{// default implementation constructs one from CRuntimeClass...//CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();...//AddDocument(pDocument);//将动态生成的文档对象的指针加入到应用程序的文档列表中return pDocument;}CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();这一句就是动态产生的核心,它借助于CRuntimeClass动态生成一个CDocument对象。其动态生成的奥秘我将在MFC技术内幕系列之(三)---《MFC执行期类型识别与动态创建技术内幕》一文中详解。
////////////////////////////////////////////////// /* 6.2.子窗口框架动态生成 */ /////////////////////////////////////////////////CMultiDocTemplate::OpenDocumentFile调用了CreateNewFrame,这就是子窗口框架动态生成的主函数。
// Default frame creationCFrameWnd* 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 fromCCreateContext context;context.m_pCurrentFrame = pOther;context.m_pCurrentDoc = pDoc;context.m_pNewViewClass = m_pViewClass;context.m_pNewDocTemplate = this; ...//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); ...//// create new from resourceif (!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;} CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();这一句就是动态产生的核心,它借助于CRuntimeClass动态生成一个CDocument对象。其动态生成的奥秘我将在MFC技术内幕系列之(三)---《MFC执行期类型识别与动态创建技术内幕》一文中详解。之后函数调用LoadFrame来创建子窗口。其过程与创建主框架窗口的过程大致相同,但也有一些不同的地方,下面我就将说说这点不同。
////////////////////////////////////////////// /* 6.3.子视图动态生成 */ ////////////////////////////////////////////// 瞪大眼睛仔细察看OpenDocumentFile的源代码,疑惑了,"怎么没有类似CView* pView =CreateNewView();的代码?","那么子视图是如何生成的呢"下面我就为你详细解释一下吧!其实子视图动态生成函数被放到另一个地方了。让我们详细来看看吧。 其实,关键还是在LoadFrame,但与创建主窗口框架的那个LoadFrame不同的是传进了一个不同的参数&context,你回过头看看主窗口框架的那个LoadFrame,调用它时使用了默认参数,而那个默认参数值为NULL,下面看看CCreateContext 结构。
- MFC技术内幕系列之(二)---MFC文档视图结构内幕 上
- MFC技术内幕系列之(二)---MFC文档视图结构内幕 上
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图结构内幕 下
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图结构内幕
- MFC技术内幕系列之(二)---MFC文档视图
- MFC文档视图结构内幕
- MFC文档视图结构内幕
- MFC文档视图结构内幕
- MFC文档视图结构内幕
- MFC文档视图结构内幕
- MFC技术内幕系列之(五)---MFC文档序列化内幕
- MFC技术内幕系列之(五)---MFC文档序列化内幕
- [转]Java 6 RowSet 使用完全剖析
- DataList嵌套DataList(2. 页面绑定后台代码使用ItemDataBound事件实现 纯代码)
- MFC技术内幕系列之(一)---MFC应用程序“生死因果”内幕
- 2005年百度之星程序设计大赛试题初赛题目四的解答(低频词过滤)
- MFC技术内幕系列之(二)---MFC文档视图结构内幕 上
- MFC技术内幕系列之(二)---MFC文档视图结构内幕 上
- Struts框架简介
- MFC技术内幕系列之(二)---MFC文档视图结构内幕 下
- SSClone 1.0 (最初无限制版)公开发布
- MFC技术内幕系列之(三)---MFC执行期类型识别与动态创建技术内幕
- MFC技术内幕系列之(四)---MFC消息映射与消息传递内幕
- MFC技术内幕系列之(五)---MFC文档序列化内幕
- DataList嵌套DataList(3. 使用DataRelation实现 纯代码)
- 先写个简历