MFC文档序列化

来源:互联网 发布:用php做购物网站案例 编辑:程序博客网 时间:2024/05/18 03:09

 

  引入“文档序列化”的概念会让许多人感到迷惑,什么是“文档序列化”?其实说白了就是将数据(广义上的概念)从硬盘中读出或将数据写入硬盘中。其稍正规的定义可以看看MFC文档如下:
The CArchive class allows you to save a complex network of objects in a permanent binary form (usually disk storage) that persists after those objects are deleted. Later you can load the objects from persistent storage, reconstituting them in memory. This process of making data persistent is called "serialization."
     在MFC中,CArchive类和CRuntimeClass结构以及一些神秘地宏共同完成“文档序列化”这一工作,当然了我们也不能忘记CFile类以及它的那些派生类的功劳。

正文:
    “文档序列化”显然可以分成两个部分“写文件”和“读文件”。我在本文中也将从这两个方面来为你挖掘文档序列化的奥秘。
                       ///////////////////////////////////////////////
                       /*  1.“写读文件”的共同基础  */
                       ////////////////////////////////////////////// 
    无论是写还是读都等借助CRuntimeClass结构以及一些神秘的宏的帮助。在前几篇文章中我们没少和CRuntimeClass结构打交道,什么MFC执行期类型识别,什么动态创建技术等等。提到的这两种技术是文档序列化的基础,下面我们就看看为什么可以这么说:
    除了与动态创建有关的成员外,在CRuntimeClass结构中还与序列化有关的重要成员有:

 

其中两个重要函数Store和Load的源代码如下:这两个函数主要写和读a runtime class description,其中包括m_lpszClassName和m_wSchema(版本号);

 

下面看看那两个神秘的宏DECLARE_SERIAL和IMPLEMENT_SERIAL吧!

 

   宏的定义验证了上面的“基础”一说;
   下面是IMPLEMENT_SERIAL进行初始化时的辅助结构AFX_CLASSINIT及函数AfxClassInit的定义:

 

 

有了前面文章的基础,在这里我就不详细将宏展开详解了,有了上面基础,我们就可以"真刀真枪"的读写文件了,比较起来“写文件”较容易,所以就让我们拿它先开刀吧!^_^

                       //////////////////////////////////////
                       /*        2.“写文件”        */
                       //////////////////////////////////////   
   大家都应该了解“写文件”的“导火索”是什么吧?你说对了"save"or"save as",下面我们就沿着这个导火索一路下行看看到底发生了什么吧?
    这里大家会发现一个小问题:那就是你在MFC应用程序向导为你做的SDI or MDI代码中找不到
"save"or"save as"功能项的处理函数,但它的功能却还能淋漓尽致的展现在你面前。这是为什么呢?原来这些函数是由CDocument类提供的。你的Doc类继承CDocument类的同时也将这些处理函数完全集成了下来。下面我们就来看看他们的卢山真面目吧!
    当你按下"save"or"save as"功能键(包括菜单中的和工具栏中的)后,应用程序将调用
CMyDoc::OnFileSave() or CMyDoc::OnFileSaveAs()(以后将只提及一个),但由于CMyDoc类继承了其基类
CDocument的处理函数,所以实际调用的是CDocument::OnFileSave();

 

该函数创建了一个与要保存的文件相关联的CArchive实例saveArchive,并调用了函数 CMyDoc::Serialize(CArchive&ar);

 

看看该函数,你有些傻眼了,空函数几乎什么也没有,这又对了,因为MFC不知道你的数据是什么样式的,所以它没有这个能力越俎代庖。如果你没有为该函数添加代码,则该函数也就到此为止了,但我们要把其奥秘挖掘出来就不能到此结束,我们也用类似侯捷老师的Scribble例子给CMyDoc类加一点代码,改为:

 

下面我们可以继续我们的挖掘了:
   CTypedPtrList::Serialize函数被调用,由于CTypedPtrList并为改写Serialize函数,所以实际调用的是其基类CObList的Serialize函数;

 

   其中void CArchive::WriteCount(DWORD_PTR dwCount)函数用于将CObList中的表元素个书写入。
   CArchive重载了<<运算符,代码如下:

 

循线而上,你可以发现该函数最终调用的是CStroke::Serialize(CArchive&ar);
 
  也许你可能不晓得wNullTag,dwBigClassTag等之类是何东东?看看下面它们是如何定义的:
// Pointer mapping constants,in arcobj.cpp
#define wNullTag        ((WORD)0)           // special tag indicating NULL ptrs
#define wNewClassTag    ((WORD)0xFFFF)      // special tag indicating new CRuntimeClass
#define wClassTag       ((WORD)0x8000)      // 0x8000 indicates class tag (OR'd)
#define dwBigClassTag   ((DWORD)0x80000000) // 0x8000000 indicates big class tag (OR'd)
#define wBigObjectTag   ((WORD)0x7FFF)      // 0x7FFF indicates DWORD object tag
#define nMaxMapCount    ((DWORD)0x3FFFFFFE) // 0x3FFFFFFE last valid mapCount
   他们不是别的,只是一些记号,比如当你写入一个CStroke类时,它首先判断CStroke以前是否出现过,若没有,则写入 wNewClassTag  (0xFFFF),否则写入wClassTag+一定的offsets,表示这是与前面相同的旧类。 

  

在CArchive::ReadObject函数中有pOb->Serialize(*this);即调用CStroke类的Serialize函数。以此类推。
  总结:我们看看本程序到底从硬盘中读了什么?
   按顺序应该是:CTypedPtrList中的元素个数---〉新旧类标志---〉版本号(m_wSchema)---〉
                类名称字符串中的字符个数----〉类名称(ANSI码)---〉调用其它成员的                                 Serialize 函数。---〉以此类推。
  由此可以看出读入数据的顺序与写入数据时的顺序完全相同。
                       /////////////////////////////////////
                       /*         4.结局             */
                       /////////////////////////////////////
   至此,MFC技术内幕系列文章都已结束,文章中肯定有很多纰漏和错误,希望读者们批评指点。

最终调用void CRuntimeClass::Store(CArchive& ar) const;来存储class information;
  到这就"写"完了。下面总结一下:
  我们看看本程序到底向硬盘中写了什么?
  按顺序应该是:CTypedPtrList中的元素个数---〉新旧类标志---〉版本号(m_wSchema)---〉
                类名称字符串中的字符个数----〉类名称(ANSI码)---〉调用其它成员的                              Serialize 函数。---〉以此类推。
                       //////////////////////////////////////
                       /*        3.“读文件”        */
                       //////////////////////////////////////  
  看完了“写文件”,让我们看看“读文件”的内幕吧!“读文件”顾名思义,即当你打开一个文件时,应用程序从硬盘中将文件的数据读出的过程。
   当你选中“文件”菜单中的“打开”或在工具栏中单击“打开”项时,应用程序将连续调用以下序列的函数:

CWinApp::OnOpenFile()——>CDocManager::OnFileOpen()-->CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)-->CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)--->
CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)-->

 

 

 

原创粉丝点击