Serializing Strokes

When the document responds to an Open, Save, or Save As command, it delegates the real serialization work to the strokes themselves. That is, the document tells the stroke list to serialize itself, and the stroke list, in turn, tells the individual strokes to serialize themselves. As a result, all strokes in the document are read from or written to a file.

Note   Throughout the tutorial, Scribble is presented as a series of incremental versions. When you build successive versions that modify the structure of CStroke, they are incompatible with earlier versions. Attempts to read CStroke data stored by a previous version may fail because the serialization process expects a different structure. Each time you make such a modification of CStroke, tag the new version with a version number, as described in the procedure below. The version or “schema” number is checked automatically during serialization. You can check the schema number in the serialization code to support backward compatibility, allowing you to read files created with earlier versions of your application.

You’ve added serialization for Scribble documents; now you’ll implement serialization for the strokes.

To implement serialization for stroke objects

  1. Use WizardBar to jump to the CStroke constructor and, just before it, add the IMPLEMENT_SERIAL macro as shown:
    IMPLEMENT_SERIAL( CStroke, CObject, 1 )
    

    The third argument is the schema number, discussed earlier. It’s set to 1 for Scribble Step 1.

    The IMPLEMENT_SERIAL macro complements the DECLARE_SERIAL macro, which you declared in ScribbleDoc.h when you were adding the code for the CStroke class. The two macros prepare a class for serialization.

    Like CScribbleDoc, CStroke also overrides the Serialize member function of its base class. When the stroke-list object is called to serialize itself, it calls each stroke object in turn to serialize itself.

  2. Just after the second CStroke constructor (the one that initializes the pen), add the following Serialize override:
    void CStroke::Serialize( CArchive& ar )
    {
    if( ar.IsStoring( ) )
    {
    ar << (WORD)m_nPenWidth;
    m_pointArray.Serialize( ar );
    }
    else
    {
    WORD w;
    ar >> w;
    m_nPenWidth = w;
    m_pointArray.Serialize( ar );
    }
    }
    

    If the archive object is used for storing, the stroke’s pen-width value is stored in the archive and then its array of points is stored. Notice that the m_pointArray object as a CArray object can serialize itself.

    If the archive object is used for loading, the stroke’s data must be read in the same order it was written: first the pen width, then the array of points. The else branch of the if statement declares a local variable to receive the width, then copies that value to m_nPenWidth. It then calls upon the point array to load its data (see the figure Serialization in Scribble in Serializing the Data earlier in this lesson).

    Note that the m_nPenWidth variable is cast to a WORD before it’s inserted in the archive, ar:

    ar << (WORD)m_nPenWidth;
    

    The cast is necessary because m_nPenWidth is declared as type UINT (unsigned integer). The archive mechanism only supports saving types of fixed size. UINT, for example, is 16 bits in Windows 3.1 and 32 bits in Windows NT and Windows 95. Using the WORD cast makes the data files created by your application portable. To promote machine independence, class CArchive doesn’t have an extraction operator for type int but does have one for type WORD.

Once this code is in place, serialization of Scribble’s data is automatic.