Click here to Skip to main content
15,899,314 members
Articles / Desktop Programming / MFC
Article

Serialization: how to really get compatible with older application versions

Rate me:
Please Sign up or sign in to vote.
4.11/5 (6 votes)
9 Aug 20052 min read 29.1K   24   2
Saving objects in older schema versions.

Introduction

Serialization is the standard way MFC proposes to save objects into files. By the use of versionable schemas, your objects can grow and be extended and the application will still be capable of reading older files with lower schema versions.

But if you want to be able to save a file in a way that it might be readable with an older application version, you run into the limits of what MFC provides...

Situation

#define BLOCK_SCHEMA 4
IMPLEMENT_SERIAL(CBlock, CObject, BLOCK_SCHEMA|VERSIONABLE_SCHEMA)

Each class you define as DECLARE_SERIAL has a static const member of the type CRuntimeClass, and the schema version you choose for your class is hard-coded into its instantiation.

Whenever you serialize an instance of your class, its CRuntimeClass member (called classCBlock in the above example) is serialized into the archive:

void CArchive::WriteClass(const CRuntimeClass* pClassRef)
{
    ...
    this << wNewClassTag;
    ClassRef->Store(*this);
    ..

}

The CRuntimeClass implementation finally writes the schema version into the archive:

void CRuntimeClass::Store(CArchive& ar) const
{
    WORD nLen = (WORD)lstrlenA(m_lpszClassName);
    ar << (WORD)m_wSchema << nLen;
    ar.Write(m_lpszClassName, nLen*sizeof(char));

}

Now you say: "Aha, I know what he's trying to do…". Exactly. We have to find a way to change the schema version used when writing the file, in order to create a file that is compatible with an older application. But then we have to change the serialize methods of our objects as well. This is actually easier than you think…

Solution

Let's start with the Serialize method. It consists normally of two parts, the Storing part and the Loading part. The Storing part usually reads the schema version on the first line and then loads the object's members considering the schema:

void CBlock::Serialize(CArchive& ar)

{
    if (ar.IsStoring())
    {
    …

    }
    else
    {
        UINT nSchema = ar.GetObjectSchema();

        ar >> m_iType;
        ar >> m_iLines;

        if (nSchema > 3) 
            ar >> m_strDisplayName;
    }

}

Now we have to prepare a similar code for the Storing part, which takes the schema version into consideration. We know that the schema is stored in a member of the CRuntimeClass. Fortunately this member is public, so we can simply get its value (masked with VERSIONABLE_SCHEMA):

void CBlock::Serialize(CArchive& ar)

{
    if (ar.IsStoring())
    {
        UINT nSchema = classCBlock.m_wSchema & ~VERSIONABLE_SCHEMA;

        ar << m_iType;
        ar << m_iLines;

        if (nSchema > 3)
            ar << m_strDisplayName;
    }
    else
    {
        UINT nSchema = ar. GetObjectSchema();

        ar >> m_iType;
        ar >> m_iLines;

        if (nSchema > 3) 
            ar >> m_strDisplayName;
    }

}

Isn't this simple? And as you see, the Storing and Loading parts are symmetric, so it's very easy from now on to maintain this code for further schema version changes.

But now comes the more tricky part. We have to tell the CRuntimeClass that it should use a version number which is different to the one hard-coded. Since the member classCBlock is static const, we have to cast it in order to change its members. To make it even easier for me, I defined a macro which does the whole thing for me:

#define SETSCHEMA(name, schema) \
    ((CRuntimeClass*)&name::class##name)->m_wSchema = 
                                 schema | VERSIONABLE_SCHEMA

The code to change the version can now look something like this:

voidPrepareObjectSchema(int nVersion)

{
    switch (nVersion)
    {
        case VERSION3: 
        {
            SETSCHEMA(CBlock, 3);
            SETSCHEMA(CLinkedBlock, 1);
            SETSCHEMA(CParameter, 3);
            break;
        }
    case VERSION4:
    default:
        {
            SETSCHEMA(CBlock, BLOCK_SCHEMA);
            SETSCHEMA(CLinkedBlock, LINKEDBLOCK_SCHEMA);
            SETSCHEMA(CParameter, PARAMETER_SCHEMA);
            break;
        }
    }

}

Attention

It is very important that you change the schema versions back to the original values once your file is saved.

Enjoy…

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here



Comments and Discussions

 
GeneralTypo Pin
YoSilver10-Aug-05 10:59
YoSilver10-Aug-05 10:59 
GeneralAlso see... Pin
Ravi Bhavnani10-Aug-05 6:36
professionalRavi Bhavnani10-Aug-05 6:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.