To understand the ATL COM persistence implementations, let's first look at the interfaces involved and what they are designed to accomplish. Persistence involves three participants: the application, the object being persisted, and an object that handles the read/write operations on the actual data. The application initiates the load or the save by creating an object that implements the IStream, IStorage, or IPropertyBag interface. This interface pointer is then passed off to the persisted object, using one of the IPersistxxx interfaces (which the object must implement). The object then uses the interface pointer either to load or to save its state, as requested by the application. Objects can, and typically do, implement more than one persistence interface, allowing the containing application to choose its preferred method. ATL provides implementations for the IPersistStream, IPersistStreamInit, IPersistStorage, and IPersistPropertyBag interfaces. Figure 111 illustrates how the interfaces match up between the corresponding data object and persisted object.
Figure 11-1. Persistence interface pairs.
Here's a summary of the interfaces involved in ATL persistence and what they do:
- ISequentialStream Defines simple read/write semantics for a sequential stream of bytes. This interface is normally implemented as IStream, which derives from ISequentialStream.
- IStream Implemented on a container's storage medium to enable loading or saving a sequential stream of bytes. IStream extends ISequentialStream to add transactions, cloning, random access, and locking. COM provides the CreateStreamOnHGlobal API to create a stream object implementation on global memory. IStream is probably the most widely used data object interface in COM persistence. It can be used in conjunction with the IPersistStream and IPersistStreamInit interfaces.
- IPersist Defines a single method, GetClassID, which is used to determine the type of object being saved. This type information is used when loading the object to re-create the correct class.
- IPersistStream Derives from IPersist. Provides methods for saving to and loading from an IStream provided by the container.
- IPersistStreamInit Has all the same methods as IPersistStream, and adds the InitNew method for initializing an object to a default state. Implemented by ATL.
- IStorage Implemented on the container's storage medium. A storage can be created on a file with COM APIs. Storages are containers for streams and other storages. Microsoft Office applications use this mechanism extensively with the compound document (also known as structured storage) specification. This interface is used with IPersistStorage on the persisted object.
- IPersistStorage Implemented on an object to enable persistence using the IStorage interface. Implemented by ATL.
- IPropertyBag Specifies reading/writing persisted state as name/value pairs. The application can implement this interface to store the state in any format it chooses. The values themselves must be VARIANT-compatible types. The nice thing about property bags is that the application has control over each data value persisted, instead of a byte stream formatted by the persisted object. The other half of property bag persistence is IPersistPropertyBag.
- IPersistPropertyBag Similar to IPersistStreamInit, except an IPropertyBag* is passed as an argument to Load and Save instead of an IStream*. IPersistPropertyBag also supports error reporting through the IErrorInfo interface. Implemented by ATL.