Open File& New File之MFC后台代码分析
作者:(ECNU)孟庆涛 Email:[email protected]
Creating an Empty Document: The CWinApp::OnFileNew Function
开始运行时,CWinApp::ProcessShellCommand会自动调用OnFileNew函数。
After your application class’s InitInstance function calls the AddDocTemplate member function, it calls OnFileNew (indirectly through CWinApp::ProcessShellCommand), another important CWinApp member function. OnFileNew sorts through the web of interconnected class names and does the following:
(2)OnFileOpen
When the MFC Application Wizard generates an application, it maps the File Open menu command to the CWinApp::OnFileOpen member function. When called, this function invokes a sequence of functions to accomplish these steps:
1. Prompts the user to select a file.
2. Calls the virtual function CDocument::OnOpenDocument for the already existing document object. This function opens the file, calls CDocument::DeleteContents, and constructs a CArchive object set for loading. It then calls the document’s Serialize function, which loads data from the archive.
3. Calls the view’s OnInitialUpdate function.
(3)MFC源代码分析:
注意:表格左边是新建文件过程,右边是打开文件过程,没分左右的则是二者的功用部分。下面都是MFC
后台代码,涉及到序列化问题等。总之,自己品味,其乐无穷。:)
NEW |
Open |
|||
void CWinApp::OnFileNew() { if (m_pDocManager != NULL) m_pDocManager->OnFileNew(); } |
void CWinApp::OnFileOpen() { ASSERT(m_pDocManager != NULL); m_pDocManager->OnFileOpen(); } |
|||
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); 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 } |
void CDocManager::OnFileOpen() { // prompt the user (with all document templates) CString newName; if (!DoPromptFileName(newName, AFX_IDS_OPENFILE, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL)) return; // open cancelled AfxGetApp()->OpenDocumentFile(newName); // if returns NULL, the user has already been alerted } |
|||
无 |
CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName) { ASSERT(m_pDocManager != NULL); return m_pDocManager->OpenDocumentFile(lpszFileName); } |
|||
无 |
CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName) { // find the highest confidence POSITION pos = m_templateList.GetHeadPosition(); CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt; CDocTemplate* pBestTemplate = NULL; CDocument* pOpenDocument = NULL; TCHAR szPath[_MAX_PATH]; ASSERT(lstrlen(lpszFileName) < _countof(szPath)); TCHAR szTemp[_MAX_PATH]; if (lpszFileName[0] == '/"') ++lpszFileName; lstrcpyn(szTemp, lpszFileName, _MAX_PATH); LPTSTR lpszLast = _tcsrchr(szTemp, '/"'); if (lpszLast != NULL) *lpszLast = 0; if( AfxFullPath(szPath, szTemp) == FALSE ) { ASSERT(FALSE); return NULL; // We won't open the file. MFC requires paths with // length < _MAX_PATH } TCHAR szLinkName[_MAX_PATH]; if (AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH)) lstrcpy(szPath, szLinkName); while (pos != NULL) { CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetNext(pos); ASSERT_KINDOF(CDocTemplate, pTemplate); CDocTemplate::Confidence match; ASSERT(pOpenDocument == NULL); match = pTemplate->MatchDocType(szPath, pOpenDocument); if (match > bestMatch) { bestMatch = match; pBestTemplate = pTemplate; } if (match == CDocTemplate::yesAlreadyOpen) break; // stop here } if (pOpenDocument != NULL) { POSITION pos = pOpenDocument->GetFirstViewPosition(); if (pos != NULL) { CView* pView = pOpenDocument->GetNextView(pos); // get first one ASSERT_VALID(pView); CFrameWnd* pFrame = pView->GetParentFrame() if (pFrame == NULL) TRACE(traceAppMsg, 0, "Error: Can not find a frame for document to activate./n"); else {pFrame->ActivateFrame(); if (pFrame->GetParent() != NULL) { CFrameWnd* pAppFrame; if (pFrame != (pAppFrame = (CFrameWnd*)AfxGetApp()->m_pMainWnd)) { ASSERT_KINDOF(CFrameWnd, pAppFrame); pAppFrame->ActivateFrame(); } } } } else TRACE(traceAppMsg, 0, "Error: Can not find a view for document to activate./n"); return pOpenDocument; } if (pBestTemplate == NULL) { AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC); return NULL; } return pBestTemplate->OpenDocumentFile(szPath); }
|
|||
1: 单文档模板 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) // 对File/NEW而言 { // 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 TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE./n"); if (bCreated) pFrame->DestroyWindow(); // will destroy document return NULL; } } else //对File/Open而言 { 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 TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE./n"); 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()) { TRACE(traceAppMsg, 0, "Error: OnNewDocument failed after trying " "to open a document - trying to continue./n"); // assume we can continue } } return NULL; // open failed } pDocument->SetPathName(lpszPathName); }
CWinThread* pThread = AfxGetThread(); ASSERT(pThread); 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; } 2: 多文档模板: CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible) { CDocument* pDocument = CreateNewDocument(); if (pDocument == NULL) { TRACE(traceAppMsg, 0, "CDocTemplate::CreateNewDocument returned NULL./n"); AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC); return NULL; } ASSERT_VALID(pDocument);
BOOL bAutoDelete = pDocument->m_bAutoDelete; pDocument->m_bAutoDelete = FALSE; // don't destroy if something goes wrong CFrameWnd* 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; } ASSERT_VALID(pFrame);
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; }
|
||||
BOOL CDocument::OnNewDocument() { #ifdef _DEBUG if(IsModified()) TRACE(traceAppMsg, 0, "Warning: OnNewDocument replaces an unsaved document./n"); #endif
DeleteContents(); m_strPathName.Empty(); // no path name yet SetModifiedFlag(FALSE); // make clean return TRUE; }
|
BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName) { #ifdef _DEBUG if (IsModified()) TRACE(traceAppMsg, 0, "Warning: OnOpenDocument replaces an unsaved document./n"); #endif CFileException fe; CFile* pFile = GetFile(lpszPathName, CFile::modeRead|CFile::shareDenyWrite, &fe); if (pFile == NULL) { ReportSaveLoadException(lpszPathName, &fe, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); return FALSE; } DeleteContents(); SetModifiedFlag(); // dirty during de-serialize CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete); loadArchive.m_pDocument = this; loadArchive.m_bForceFlat = FALSE; TRY { CWaitCursor wait; if (pFile->GetLength() != 0) Serialize(loadArchive); // load me loadArchive.Close(); ReleaseFile(pFile, FALSE); } CATCH_ALL(e) { ReleaseFile(pFile, TRUE); DeleteContents(); // remove failed contents TRY { ReportSaveLoadException(lpszPathName, e, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); } END_TRY DELETE_EXCEPTION(e); return FALSE; } END_CATCH_ALL SetModifiedFlag(FALSE); // start off with unmodified return TRUE; }
|
|||
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); if(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*)(UINT_PTR)m_nCmdShow; m_nCmdShow = SW_HIDE; break;
// If we've been asked to register, exit without showing UI. // Registration was already done in InitInstance(). case CCommandLineInfo::AppRegister: { BOOL bRegistered = Register(); bResult = FALSE; if (!rCmdInfo.m_bRunEmbedded) { bRegistered; // if (bRegistered) // AfxMessageBox(AFX_IDP_REG_DONE); // else // AfxMessageBox(AFX_IDP_REG_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; }
// If we've been asked to unregister, unregister and then terminate case CCommandLineInfo::AppUnregister: { 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; } |
||||
|
||||
When the MFC Application Wizard generates an application, it maps the File Save menu command to the OnFileSave member function of the CDocument class. OnFileSave calls the CDocument function OnSaveDocument, which in turn calls your document’s Serialize function with an archive object set for storing. The File Save As menu command is handled in a similar manner: It is mapped to the CDocument function OnFileSaveAs, which calls OnSaveDocument. Here the application framework does all the file management necessary to save a document on disk.
Save |
Save As |
void CDocument::OnFileSave() { DoFileSave(); } |
void CDocument::OnFileSaveAs() { if(!DoSave(NULL)) TRACE(traceAppMsg, 0, "Warning: File save-as failed./n"); } |
BOOL CDocument::DoFileSave() { DWORD dwAttrib = GetFileAttributes(m_strPathName); if (dwAttrib & FILE_ATTRIBUTE_READONLY) { // we do not have read-write access or the file does not (now) exist if (!DoSave(NULL)) { TRACE(traceAppMsg, 0, "Warning: File save with new name failed./n"); return FALSE; } } else { if (!DoSave(m_strPathName)) { TRACE(traceAppMsg, 0, "Warning: File save failed./n"); return FALSE; } } return TRUE;} |
无 |
BOOL CDocument::DoSave(LPCTSTR lpszPathName, BOOL bReplace) // Save the document data to a file // lpszPathName = path name where to save document file // if lpszPathName is NULL then the user will be prompted (SaveAs) // note: lpszPathName can be different than 'm_strPathName' // if 'bReplace' is TRUE will change file name if successful (SaveAs) // if 'bReplace' is FALSE will not change path name (SaveCopyAs) { CString newName = lpszPathName; if (newName.IsEmpty()) { CDocTemplate* pTemplate = GetDocTemplate(); ASSERT(pTemplate != NULL);
newName = m_strPathName; if (bReplace && newName.IsEmpty()) { newName = m_strTitle; // check for dubious filename int iBad = newName.FindOneOf(_T(":///")); if (iBad != -1) newName.ReleaseBuffer(iBad);
// append the default suffix if there is one CString strExt; if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) && !strExt.IsEmpty()) { ASSERT(strExt[0] == '.'); int iStart = 0; newName += strExt.Tokenize(_T(";"), iStart); } } if (!AfxGetApp()->DoPromptFileName(newName, bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate)) return FALSE; // don't even attempt to save } CWaitCursor wait; if (!OnSaveDocument(newName)) { if (lpszPathName == NULL) { // be sure to delete the file TRY { CFile::Remove(newName); } CATCH_ALL(e) { TRACE(traceAppMsg, 0, "Warning: failed to delete file after failed SaveAs./n"); DELETE_EXCEPTION(e); } END_CATCH_ALL } return FALSE; } // reset the title and change the document name if (bReplace) SetPathName(newName); return TRUE; // success } |
|
BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName) { CFileException fe; CFile* pFile = NULL; pFile = GetFile(lpszPathName, CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive, &fe); if (pFile == NULL) { ReportSaveLoadException(lpszPathName, &fe, TRUE, AFX_IDP_INVALID_FILENAME); return FALSE; } CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete); saveArchive.m_pDocument = this; saveArchive.m_bForceFlat = FALSE; TRY { CWaitCursor wait; Serialize(saveArchive); // save me saveArchive.Close(); ReleaseFile(pFile, FALSE); } CATCH_ALL(e) { ReleaseFile(pFile, TRUE); TRY { ReportSaveLoadException(lpszPathName, e, TRUE, AFX_IDP_FAILED_TO_SAVE_DOC); } END_TRY DELETE_EXCEPTION(e); return FALSE; } END_CATCH_ALL SetModifiedFlag(FALSE); // back to unmodified return TRUE; // success } |