In Chapter 7, we added basic compound file functionality to Patron. This enabled a document to maintain a list of pages, and each page committed its storage before the end user switched away from it. When we saved the entire document, we wrote a "Page List" stream off the root storage that contained the number of pages in the document, the currently viewed page, the next ID to use for a page, and the list of pages—that is, the list of page IDs from which Patron can re-create the name of the page's storage.
For this chapter, we extend the same idea to each page. A page can maintain a list of tenants and write a stream containing the number of tenants, the next tenant ID, and a list of tenant IDs that exist in the page. We base the name of the tenant's storage on the tenant ID, just as we do for the pages. Managing the storages, however, is a little different. When the page is open, all the tenants on that page are also considered loaded in the sense that Patron has a pointer to the object in that tenant. When you switch away from the page, each tenant's storage is committed before the page's storage is committed. Still, nothing is written to the disk because we have yet to commit the root storage. But as far as each page is concerned, we don't have to try to keep pointers to any tenant that has been modified; instead, we save those objects to storage when closing the page and reload them from memory when the page is reopened.
In the Freeloader sample in Chapter 11, we explicitly used the object's IPersistStorage interface to affect the saving and loading of the objects and their presentations. Patron instead uses OleSave and OleLoad—OLE API function wrappers—which reproduce exactly, and I mean exactly (I looked), the sequence of operations that we performed in Freeloader. OleSave saves all the presentations in the cache and stores the object's class ID to its storage. OleLoad reinitializes the cache from the saved presentations and creates a pointer to the object. When we reload a tenant and its object, we do not need to use Create. That latter function is exclusively for first-time creation of the object residing in the tenant.
Patron's whole storage scheme really shows off the power of transactioned storage. By simple virtue of having the root storage transacted, we can write the rest of the application to think that its data is always on disk. In other words, when we create a new object in a tenant, we immediately save that object to its storage. (See the OleSave call in CTenant::CreateStatic.) When any tenant is asked to update itself in its storage, it writes a small stream containing its FORMATETC and position information and then calls OleSave to write all the messy data, followed by a Commit. In all, the storage management on the tenant level is minimal, and the page needs only to ensure that each tenant is given the chance to update itself before the page closes.
The most beautiful part of this storage mechanism is that we now have in place everything we need to handle storage for a compound document content object. When we enable this feature in Patron in Chapter 17, you'll see that we need no modifications to our storage model.