MFC文檔序列化

 

  引入“文檔序列化”的概念會讓許多人感到迷惑,什麼是“文檔序列化”?其實說白了就是將數據(廣義上的概念)從硬盤中讀出或將數據寫入硬盤中。其稍正規的定義可以看看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)-->

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章