当用户在界面上选择文件菜单/打开文件(ID_FILE_OPEN)时,CWinApp派生类的OnFileOpen函数被自动调用,它通过文档模板创建(MDI)/重用(SDI)框架、文档和视图对象,并最终调用CDocument::OnOpenDocument来读文件,CDocument::OnOpenDocument 的处理流程如下:
//示例代码4 BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName) { if (IsModified()) TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");
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; } |
同样,当用户选择菜单文件/文件保存(ID_FILE_SAVE)或者文件/另存为...(ID_FILE_SAVEAS)时,通过CWinApp::OnFileSave和CWinApp::OnFileSaveAs 最终调用CDocument::OnSaveDocument,这个函数处理如下:
//示例代码5 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 } |
从前面两段代码可以看出,文件读和文件写的结构基本相同,并且最终都调用了CObject::Serialize函数完成对文档自己的读和写(参见注释中的save me和load me)。对于用AppWizard自动生成的MDI和SDI,系统自动生成了这个函数的重载实现,缺省的实现为:
//示例代码6 void CMyDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } } |
如果一个对VC非常熟悉的人,喜欢手工生成所有的代码(当然这是非常浪费时间也是没有必要的),那么他提供的CDocument派生类也应该实现这个缺省的Serialize函数,否则,系统在文件读写时只能调用CObject::Serialize,这个函数什么都不做,当然也无法完成对特定对象的文件保存/载入工作。当然,用户也可以截获ID_FILE_OPEN等菜单,实现自己的文件读写功能,但是这样的代码将变得非常烦琐,也不容易阅读。
回到CMyDoc::Serialize函数。这个函数通过对ar对象的判断,决定当前是在读还是在写文件。由于AppWizard不知道你的文档是干什么的,所以它不会给你添加实际的文件读写代码。假设你的文档中有三个对象m_Obj_a,m_Obj_b,m_Obj_c,那么实际的代码应该为:
//示例代码7 void CMyDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { ar << m_Obj_a << m_Obj_b << m_Obj_c; } else { ar >> m_Obj_a >> m_Obj_b >> m_Obj_c; } } |
|