MFC文档视图结构框架分析 1:程序的“导火索”---theApp CmyApp theApp; 在声明对象的同时,调用其构造函数。按C++的语法,首先要调用其基类Cwinapp的构造函数. 这个文件主要用于应用程序的一些初始化操作。 class CWinApp : public CWinThread { DECLARE_DYNAMIC(CWinApp) public: // Constructor CWinApp(LPCTSTR lpszAppName = NULL); ………… } CWinApp::CWinApp(LPCTSTR lpszAppName) { if (lpszAppName != NULL) m_pszAppName = _tcsdup(lpszAppName); else m_pszAppName = NULL; // initialize CWinThread state AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; ASSERT(AfxGetThread() == NULL); pThreadState->m_pCurrentWinThread = this; ASSERT(AfxGetThread() == this); m_hThread = ::GetCurrentThread(); m_nThreadID = ::GetCurrentThreadId(); // initialize CWinApp state ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please pModuleState->m_pCurrentWinApp = this; ASSERT(AfxGetApp() == this); // in non-running state until WinMain m_hInstance = NULL; m_hLangResourceDLL = NULL; m_pszHelpFilePath = NULL; m_pszProfileName = NULL; m_pszRegistryKey = NULL; m_pszExeName = NULL; m_pRecentFileList = NULL; m_pDocManager = NULL; m_atomApp = m_atomSystemTopic = NULL; m_lpCmdLine = NULL; m_pCmdInfo = NULL; // initialize wait cursor state m_nWaitCursorCount = 0; m_hcurWaitCursorRestore = NULL; // initialize current printer state m_hDevMode = NULL; m_hDevNames = NULL; m_nNumPreviewPages = 0; // not specified (defaults to 1) // initialize DAO state m_lpfnDaoTerm = NULL; // will be set if AfxDaoInit called // other initialization m_bHelpMode = FALSE; m_eHelpType = afxWinHelp; m_nSafetyPoolSize = 512; // default size } 2:theApp之后的隐藏代码,由他控制整个程序的
。 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其中有宏定义:#define _tWinMain wWinMain int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL); int nReturnCode = -1; CWinThread* pThread = AfxGetThread();;// CWinApp是从CWinThread派生的, CWinApp* pApp = AfxGetApp(); //实质上就是pThread==pApp // AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) //用于初始化 goto InitFailure; // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) //用于初始化 goto InitFailure; // Perform specific initializations if (!pThread->InitInstance()) //注意多态性 virtual BOOL InitInstance(); //又因为pThread==pApp,所以调用pApp-> InitInstance() { if (pThread->m_pMainWnd != NULL) { TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n"); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run(); //控制消息循环 InitFailure: #ifdef _DEBUG // Check for missing AfxLockTempMap calls if (AfxGetModuleThreadState()->m_nTempMapLock != 0) { TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock); } AfxLockTempMaps(); AfxUnlockTempMaps(-1); #endif AfxWinTerm(); return nReturnCode; } 由上面的程序可以看到几个很重要的函数 (1)AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure; (2) pApp->InitApplication()) (3) pThread->InitInstance() (4) pThread->Run() 其中1,2 也是完成程序的一些初始化工作,4 主要是为了处理消息,3呢,很关键,我们运行时看到的窗口就是从这里产生。下面一一介绍 3:程序自动产生的InitInstance()函数 以下是自动生成的InitInstance()源程序: BOOL CmyApp::InitInstance() { // 如果一个运行在 Windows XP 上的应用程序清单指定要 // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式, //则需要 InitCommonControls()。否则,将无法创建窗口。 InitCommonControls(); CWinApp::InitInstance(); // 初始化 OLE 库 if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); // 标准初始化 // 如果未使用这些功能并希望减小 // 最终可执行文件的大小,则应移除下列 // 不需要的特定初始化例程 // 更改用于存储设置的注册
项 // TODO: 应适当修改该字符串, // 例如修改为公司或组织名 SetRegistryKey(_T("应用程序向导生成的本地应用程序")); LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU) // 注册应用程序的文档
。文档模板 // 将用作文档、框架窗口和视图之间的连接 CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(IDR_myTYPE, RUNTIME_CLASS(CmyDoc), RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CmyView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); // 创建主 MDI 框架窗口 CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame || !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; } 其中,注册窗口用到了一下函数,比较长,如下: BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister) { // mask off all classes that are already registered AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); fToRegister &= ~pModuleState->m_fRegisteredClasses; if (fToRegister == 0) return TRUE; LONG fRegisteredClasses = 0; // common initialization WNDCLASS wndcls; memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults wndcls.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 go if (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_WNDOLECONTROL_REG) { // OLE Control windows - use parent DC for speed wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.lpszClassName = _afxWndOleControl; if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WNDOLECONTROL_REG; } if (fToRegister & AFX_WNDCONTROLBAR_REG) { // Control bar windows wndcls.style = 0; // control bars don't handle double click wndcls.lpszClassName = _afxWndControlBar; wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WNDCONTROLBAR_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; } if (fToRegister & AFX_WNDCOMMCTLS_REG) { // this flag is compatible with the old InitCommonControls() API init.dwICC = ICC_WIN95_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK); fToRegister &= ~AFX_WIN95CTLS_MASK; } if (fToRegister & AFX_WNDCOMMCTL_UPDOWN_REG) { init.dwICC = ICC_UPDOWN_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_UPDOWN_REG); } if (fToRegister & AFX_WNDCOMMCTL_TREEVIEW_REG) { init.dwICC = ICC_TREEVIEW_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TREEVIEW_REG); } if (fToRegister & AFX_WNDCOMMCTL_TAB_REG) { init.dwICC = ICC_TAB_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TAB_REG); } if (fToRegister & AFX_WNDCOMMCTL_PROGRESS_REG) { init.dwICC = ICC_PROGRESS_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PROGRESS_REG); } if (fToRegister & AFX_WNDCOMMCTL_LISTVIEW_REG) { init.dwICC = ICC_LISTVIEW_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LISTVIEW_REG); } if (fToRegister & AFX_WNDCOMMCTL_HOTKEY_REG) { init.dwICC = ICC_HOTKEY_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_HOTKEY_REG); } if (fToRegister & AFX_WNDCOMMCTL_BAR_REG) { init.dwICC = ICC_BAR_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_BAR_REG); } if (fToRegister & AFX_WNDCOMMCTL_ANIMATE_REG) { init.dwICC = ICC_ANIMATE_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_ANIMATE_REG); } if (fToRegister & AFX_WNDCOMMCTL_INTERNET_REG) { init.dwICC = ICC_INTERNET_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_INTERNET_REG); } if (fToRegister & AFX_WNDCOMMCTL_COOL_REG) { init.dwICC = ICC_COOL_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_COOL_REG); } if (fToRegister & AFX_WNDCOMMCTL_USEREX_REG) { init.dwICC = ICC_USEREX_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_USEREX_REG); } if (fToRegister & AFX_WNDCOMMCTL_DATE_REG) { init.dwICC = ICC_DATE_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_DATE_REG); } if (fToRegister & AFX_WNDCOMMCTL_LINK_REG) { init.dwICC = ICC_LINK_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LINK_REG); } // save new state of registered controls pModuleState->m_fRegisteredClasses |= fRegisteredClasses; // special case for all common controls registered, turn on AFX_WNDCOMMCTLS_REG if ((pModuleState->m_fRegisteredClasses & AFX_WIN95CTLS_MASK) == AFX_WIN95CTLS_MASK) { pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG; fRegisteredClasses |= AFX_WNDCOMMCTLS_REG; } // must have registered at least as mamy classes as requested return (fToRegister & fRegisteredClasses) == fToRegister; } 他是在PreCreateWindow中调用的注册, 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; } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return TRUE; } BOOL CView::PreCreateWindow(CREATESTRUCT & cs) { ASSERT(cs.style & WS_CHILD); if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background } if (cs.style & WS_BORDER) { cs.dwExStyle |= WS_EX_CLIENTEDGE; cs.style &= ~WS_BORDER; } return TRUE; } BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return CView::PreCreateWindow(cs); } 有几点要
(1)m_pMainWnd,头文件里并没有这个变量呀? 噢,这个在基类里定义的public 类型的变量,所以,他是通过继承得到的。 (2) 你注意过吗,当我们一点运行,会默认出来一个视图窗口,这是谁调用的呢?哦,就是下面这几行,它调用了opendocument() 函数。 CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE; (3)这个函数有个关键的地方 CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(IDR_myTYPE, RUNTIME_CLASS(CmyDoc), RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CmyView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); 通过它,把视图,窗口和文档结合起来。 4:主窗口CMainFrame的创建 (1)通过CMainFrame* pMainFrame = new CMainFrame; 我们得到的是不断调用的基类 CMainFrame::CMainFrame() { // TODO: 在此添加成员初始化代码 } CMDIFrameWnd::CMDIFrameWnd() { m_hWndMDIClient = NULL; } CFrameWnd::CFrameWnd() { ASSERT(m_hWnd == NULL); m_nWindow = -1; // unknown window ID m_bAutoMenuEnable = TRUE; // auto enable on by default m_lpfnCloseProc = NULL; m_hMenuDefault = NULL; m_hAccelTable = NULL; m_nIDHelp = 0; m_nIDTracking = 0; m_nIDLastMessage = 0; m_pViewActive = NULL; m_cModalStack = 0; // initialize modality support m_phWndDisable = NULL; m_pNotifyHook = NULL; m_hMenuAlt = NULL; m_nIdleFlags = 0; // no idle work at start m_rectBorder.SetRectEmpty(); m_bHelpMode = HELP_INACTIVE; // not in Shift+F1 help mode m_dwPromptContext = 0; m_pNextFrameWnd = NULL; // not in list yet m_bInRecalcLayout = FALSE; m_pFloatingFrameClass = NULL; m_nShowDelay = -1; // no delay pending AddFrameWnd(); } (2)pMainFrame->LoadFrame(IDR_MAINFRAME)所引发的一系列 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 present ASSERT(m_hWnd != NULL); m_hMenuDefault = ::GetMenu(m_hWnd); if (m_hMenuDefault == NULL) TRACE(traceAppMsg, 0, "Warning: CMDIFrameWnd without a default menu.\n"); return TRUE; } 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); CString strTitle = m_strTitle; if (!Create(lpszClass, strTitle, 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); return TRUE; } 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没有重新写CreateEX,所以是调用的基类的CreateEx的函数: 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); 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) { 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; } 可以看到,::CreateWindowEx是一个全局的函数,在其中触发WM_CREATE消息,进而调用我们自己定义的CMainFrame::OnCreate() 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; } 5:运行后为什么能产生CMainFarme,文档 ,视图以及视图外包围的farme呢? 你注意过吗,当我们一点运行,会默认出来一个视图窗口,这是谁调用的呢?哦,就是下面这几行,它调用void CWinApp::OnFileNew()函数. CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE; 你可以自己跟踪以下。这里有个小技巧,你可以在BOOL CmyDoc::OnNewDocument()处设立断点,然后“跳出”,最后你会达到起始点!ProcessShellCommand(cmdInfo) void CWinApp::OnFileNew() { if (m_pDocManager != NULL) m_pDocManager->OnFileNew(); //接着调用下面的函数 } void CDocManager::OnFileNew() { if (m_templateList.IsEmpty()) { TRACE(traceAppMsg, 0, "Error: no document templates registered with CWinApp.\n"); 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); I