Serialization

 

Throughout many of the MFC classes, a Serialize function is present that has the responsibility for saving and loading that document to and from a disk file. This example of polymorphism is introduced in the CObject class and is implemented for most of the classes derived from it.

The CDocument class, being the class responsible for data saving and loading, also implements a Serialize function, but it's an empty function that AppWixard creates for you. It's your job to place in this function the code needed to save and restore any persistant data you want.


Serialize
The skeleton Serialize function is created as:


void CappDoc::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}
}



It's parameter, ar, is a CArchive object which has already been open to the source or destination file for reading or writing. MFC will open the file in response to the user's File/Save or File/Open menu selection, then pass the open file to this function as ar.

The code
if( ar.IsStoring() ) , servers as the method to determine if you are being asked to store data or not (if not, your loading). You can then use the CArchive member functions to store and load the data.


Making your custom objects serializable
In order for your custom classes to take advantage of serialization, you must implement the following items for your class:

  1. Your class must be derived from CObject.
  2. Your class must provide it's own Serialize function, like CDocument. (Call base classes Serialize if needed)
  3. Use the DECLARE_SERIAL macro in your class definition:
    DECLARE_SERIAL( class_name ) // Placed in your class definition (the .h file)
  4. Define a default constructor that takes no arguments.
  5. Use the IMPLEMENT_SERIAL macro in the implementation of your class:
    IMPLEMENT_SERIAL( class_name, base_class_name, wSchema )

 

CArchive and CFile
A CArchive is not derived from a CFile, but it's constructor needs a valid CFile as a parameter. The CArchive object implements it's own set of << and >> operators, like cin and cout do. The CFile object, is more designed for reading and writing blocks of data (binary access, not text).

Normally, MFC will create the CArchive for you. But, there may be times you want to create your own. For example: Imagine your creating a program to edit HTML code, for a web browser. You want the user to be able to store the document (an HTML file) into a temporary file and then launch a web browser with that temporary file. You don't want the user to have to save their changes to the real file however, because they only want to test their changes. So, you would create a CFile to a temporary file, then create a CArchive from that, and then invoke the Document's Serialize routine yourself. For example (this code has not been tested):

void CappView::OnViewWithBrowser()
{
	CFile Tmp;
	if( Tmp.Open("C:\\TEMP\\TEST.HTML", CFile::modeWrite, NULL ) )
	{
		CArchive TmpArc( &Tmp, CArchive::store );
		GetDocument()->Serialize( &TmpArc ); // Serialize to temporary file
		TmpArc.Close();
		Tmp.Close();
		// Launch system's default browser:
		ShellExecute( this->m_hWnd, "OPEN", "C:\\TEMP\\TEST.HTML", NULL, NULL, SW_NORMAL );
	}
	else
		MessageBox( "Error creating temporary viewing file" );
}



Serialization Example

Mflat.h

class MFlatFile : public Cfile // Step 1: derived from CObject
{
public:
	DECLARE_SERIAL( MFlatFile )	// Step 3: Use DECLARE_SERIAL
	MFlatFile() { RecordSize=0; }	// Step 4: Default constructor
	void Serialize( CArchive& ar );	// Step 2: We provide a Serialize
protected:
	CObArray FldList;
	int RecordSize;
}; 


Mflat.cpp

IMPLEMENT_SERIAL( MFlatFile, CFile, 1 )	//Step 5: use IMPLEMENT_SERIAL

void MFlatFile::Serialize( CArchive & ar )
{
	CFile::Serialize(ar); // Call Base class's Serialize routine
	FldList.Serialize( ar ); // Call Serialize for each data member (that supports it)
	if( !ar.IsStoring() ) // We don't serialize RecordSize, it is re-calculated when a file is loaded
	{
		RecordSize=0;
		for(int i=0; i < GetFieldCount(); i++ )
			RecordSize += GetField(i)->GetSize();
	}
}



Note: The FldList data member is a collection class. When it is serialized for storing, it will save the number of elements it contains, and then iterate through each element calling that elements Serialize routine. The reverse is done on serializing for loading. This means that the CObArray class implements the basic serialize ability, but ultimately whatever class it maintains must also be serializable.