Persistent Object Interfaces

When a client wants to ask an object whether it can read or write its persistent data to some type of medium—either a storage, a stream, or a file—that client queries for one of four interfaces that begin with IPersist and, in fact, derive from the IPersist interface we saw first in Chapter 6. All of these interfaces can be marshaled between processes:2

IPersistStorage

The object can read and write its information in a storage hierarchy in which the object is allowed to create additional substorages and streams to any depth it requires. The object can also open elements and hold their pointers outside the scope of calls to this interface.

IPersistStream

The object can read and write its information in a single stream and must do so within the scope of calls to this interface.

IPersistStreamInit

Same as IPersistStream, but this interface means the object would like to be informed when it is newly created and has no persistent state as yet. The member function for this purpose does not exist in IPersistStream.

IPersistFile

The object can read and write its information into a completely separate file (traditional or compound) outside the scope of Structured Storage.


All of these interfaces are considered initialization interfaces as described in Chapter 5. Each has specific member functions that perform the initialization; each one has a Load member for this purpose, and IPersistStorage and IPersistStreamInit have an additional InitNew member to distinguish new initializations from an initialization based on existing data. This existing data does not need to have come from a previous instantiation of the object—a client is perfectly free to create the data itself in a storage hierarchy, a stream, or a file prior to calling any initialization function. As long as the client knows about the object's persistent state structures, the client can fabricate data and initialize objects from the object all it wants.3

Each interface is derived from IPersist, so they all share the GetClassID member function. This call identifies the CLSID of the code that knows how to work with the persistent data. This is particularly useful for OLE, which uses the CLSID to determine what proxy or handler to load in another process that can also work with the data, as we saw in Chapter 6.

GetClassID is not the only member function that these interfaces have in common. All the interfaces also share two common operations: Load and Save. We'll see these functions as we look at IPersistStream[Init], then IPersistFile, and finally IPersistStorage, which has a more complex behavior than the others, introducing a specific set of rules that both object and client must follow.

2 At the time of writing IPersistStreamInit did not have marshaling support. Check your current system.

3 An instance of this technique is the function OleCreateFromFile, which we'll use in Chapters 17 and 20 to create embedded and linked compound document objects based on the contents of an existing file. It doesn't mean that such objects had to exist before: we're using the file contents for initialization purposes.