Solved

use of wSchema in IMPLEMENT_SERIAL

Posted on 1999-01-19
1
959 Views
Last Modified: 2013-11-20
I'd like to know how to use the schema number in in IMPLEMENT_SERIAL. I'd like to use it to be able to read files of various versions, converting each version to the latest version, e.g., read (serialize) a CArchive object. If one of the objects is a prior version, read it the old way, then convert to the latest format, the store it using the latest format.

My documentation says only that a CArchiveException will be thrown when the framework encounters an object with a schema mismatch. I don't know how to recover and 'reread' the object (to convert its format) in the CATCH block.

Any ideas?
0
Comment
Question by:ALBEARD
1 Comment
 

Accepted Solution

by:
ramrocket earned 100 total points
ID: 1327986
Here is an article that will solve your problem from MS Developers Journal.  

Good Luck!
ramrocket

Mismatched schemas

If a file is loaded containing an original CChild object, with its old schema value 0x0099, MFC uses the extraction operator and detects that the old schema value is different from the current code and throws a CArchiveException.

It is a good idea to catch a CArchiveException in the object's Serialize() implementation. This locates the source of a version incompatibility immediately:

if (ar.IsLoading())
{
      try
      {
            ar >> pChild;
      }
      catch(CArchiveException* e)
      {
            if (CArchiveException::badSchema == e->m_cause)
            {
                  // Incompatible schema number!
                  TRACE("Bad schema at %s(%d)\n", __FILE__, __LINE__);
                  // handle accordingly...
            }
            else
            {
                  // hmm, another cause...
            }
      }
}
 


Versionable objects


The basic schema technique is adequate for objects that do not exist in multiple versions. There are two solutions to supporting backward-compatible objects: implement your own versioning scheme or use MFC's VERSIONABLE_SCHEMA mechanism:


MFC versionable objects are easily defined. Just override the VERSIONABLE_SCHEMA macro with your schema number in IMPLEMENT_SERIAL:

      IMPLEMENT_SERIAL(CChild, CObject, VERSIONABLE_SCHEMA | 0x009A)
 


This deceptively simple change makes your objects backward compatible. CChild::Serialize() can now be rewritten as follows:

void CChild::Serialize(CArchive& ar)
{
      CObject::Serialize(ar);            // remember to do this
      if (ar.IsStoring)
      {
            ar << m_dwData;
            ar << m_wId;
      }
      else      // IsLoading()
      {
            switch (ar.GetObjectSchema())
            {
                  case 0x0099:      // load old version
                        ar >> m_dwData;
                        break;
                  case 0x009A:      // load current version
                        ar >> m_dwData;
                        ar >> m_wId;
                        break;
                  default:            // Ouch! Corrupted file!
                        // handle error...
            }
      }
}
 


Note the addition of the CArchive::GetObjectSchema() call. This function returns the schema for the current object.

There is a caveat with GetObjectSchema(): you may call it only once during the serialization of an object; subsequent calls return -1. Therefore, you can't use VERSIONABLE_SCHEMA to make versionable class hierarchies. The following example illustrates this point.

class CBase : public CObject { ... };
class CChild : public CBase { ... };

// Note: base and child classes have different schema numbers
IMPLEMENT_SERIAL(CBase, CObject, VERSIONABLE_SCHEMA | 0x000A);
IMPLEMENT_SERIAL(CChild, CBase, VERSIONABLE_SCHEMA | 0x000B);

void CBase::Serialize(CArchive& ar)
{
      if ( ar.IsLoading() )
      {
            UINT nSchema = ar.GetObjectSchema();      // returns 0x000A
            // load versionable object...
      }
}

void CChild::Serialize(CArchive& ar)
{
      CBase::Serialize(ar);      // call base class Serialize()
      if ( ar.IsLoading() )
      {
            UINT nSchema = ar.GetObjectSchema();      // returns -1
            // second call to GetObjectSchema() FAILS
      }
}
 

The call to GetObjectSchema() in the child class's Serialize() fails because it was already called in the base class code.

Work around this caveat by implementing your own versioning. The following code shows one simple method:

class CBase : public CObject { enum { verBase = 0x0A }; ... };
class CChild : public CBase { enum { verChild = 0x0B }; ... };

void CBase::Serialize(CArchive& ar)
{
      CObject::Serialize(ar);
      if ( ar.IsStoring() )
      {
            ar << verBase;            // explicitly save version
            // ... then save everything else
      }
      else
      {
            UINT nMySchema;            // load version number
            ar >> nMySchema;
            switch ( nSchema )
            {
                  case verBase:      //load current version of object
                        // load CBase data...
                  // add cases for older versions
            }
      }
}

void CChild::Serialize(CArchive& ar)
{
      CBase::Serialize(ar);
      if ( ar.IsStoring() )
      {
            ar << verChild;            // explicitly save version
            // ... then save everything else
      }
      else
      {
            UINT nMySchema;            // load version number
            ar >> nMySchema;
            switch ( nSchema )
            {
                  case verChild:      //load current version of object
                        // load CChild data...
                  // add cases for older versions
            }
      }
}
 


Another GetObjectSchema() caveat: Calling GetObjectSchema() within a direct call to Serialize() will generate a CArchiveException during load. The archiving operators must be used for the MFC version control to work.

It is possible to handle different schema numbers within a class hierarchy without resorting to custom versioning. CArchive contains a method SerializeClass(), which can be used to serialize base class information inside a Serialize() function:

void CChild::Serialize(CArchive& ar)
{
      // serialize base class version and run-time class info
      ar.SerializeClass(RUNTIME_CLASS(CBase));
      // serialize base class data
      CBase::Serialize(ar);
      // etc.
}
 

0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
sameEnds challenge 3 134
withoutTen challenge 14 99
EvenOdd challenge 10 90
Line meaning 9 82
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.

911 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now