好久没有写文章了.@_@!!!,每次写文章都是这句开头的,悲剧.
创建空白文档失败的原因
我们来跟踪下代码的执行:
1. CSingleDocTemplate* pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CQTimerDoc),
RUNTIME_CLASS(CMainFrame),RUNTIME_CLASS(CQTimerView));
单步跟踪,最终到达这儿:
CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
{
ASSERT_VALID_IDR(nIDResource);
…
m_nIDResource = nIDResource;
。。。
}
这儿要求Resource必须是有效的,下面会看到这个id的用途
2. 继续向下调试,到这儿
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
看下cmdInfo的字段m_nShellCommand值:FileNew
3. 跟进ProcessShellCommand
case CCommandLineInfo::FileNew:
if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
OnFileNew();
if (m_pMainWnd == NULL)
bResult = FALSE;
break;
我们可以知道在这几行代码中创建了主窗体,并赋值给了m_pMainWnd
4. 重载OnCmdMsg,让它返回FALSE以便于我们进入到CWinApp::OnFileNew中
这并不重要,继续跟踪将会到达这儿:
CDocument* CSingleDocTemplate::OpenDocumentFile(。。。。。)
{
if (m_pOnlyDoc != NULL)
{
pDocument = m_pOnlyDoc;
pFrame = (CFrameWnd*)AfxGetMainWnd();
}
else
pDocument = CreateNewDocument();
if (pDocument == NULL)
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);
if (pFrame == NULL)
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
}
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC)就是你看到的“创建空文档失败”的对话框了。
从上面的代码中可以看到造成“创建空文档失败”的原因有两个:创建Document失败和创建Frame失败。现在来着重看下第二个,第一个的分析也类似。
上面的函数中的这句: CreateNewFrame(pDocument, NULL);就是负责创建主窗体的
CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{
if (!pFrame->LoadFrame(m_nIDResource,……..)
return NULL;
}
这个LoadFrame会调用Create函数,其参数时这样的:
if (!Create(……,ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext))
return FALSE; // will self destruct on failure normally
上面的这个nIDResource就是传递给Create的Menu菜单参数,用于加载主窗体的菜单的。
BOOL CFrameWnd::Create(… , LPCTSTR lpszMenuName,)
{
if (lpszMenuName != NULL)
{
if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
return FALSE;
}
看到这儿问题的答案就很明显了::假如你的菜单的ID不是0而它又是无效菜单的ID,那就创建空文档失败了。事实上它是创建主创体失败了。
到这儿解决的办法倒也极其简单:不执行进if语句不就完了么!那就需要lpszMenuName值为NULL,追根溯源就是CSingleDocTemplate(IDR_MAINFRAME,。。。。)第一个参数传进去0,但是这种方法只能是在程序的Release版本中使用,原因自然是在上面分析的第一步中的那个ASSERT啦。
另外一种方法是:传给它一个有效的菜单ID,如果你又不想让它显示菜单,就重载PreCreateWindow, 并且写cs.hMenu = 0;