OLE Structured Storage is a specification of a "file system within a file" in which storage elements (objects with the IStorage interface) act like directories and stream elements (objects with the IStream interface) act like files. There are strong parallels between the capabilities of each element and the same type of element in a file system. The motivation behind this technology is the need for multiple components constituting an application to share an underlying disk file in the same way that multiple applications running on the same computer need to share the underlying storage device. Besides meeting this fundamental need, Structured Storage also provides a powerful way for an application to deal with its own files, in which it can benefit from incremental access, a hierarchy of named elements (of which some named elements are reserved), and built-in transactioning. By giving up absolute control over the disk file format, an application greatly benefits from the "file system within a file," especially because you don't need to run applications anymore in order to browse the elements of data within a file. This difference produces a number of significant and useful side effects, such as a "Summary Information" property set (described in Chapter 16) that could be used to run shell-level document search queries.
OLE's implementation of this storage architecture is called Compound Files, the ANSI C++ implementation of which can be licensed from Microsoft for porting to other platforms (to retain application file format compatibility). This implementation supports the Structured Storage features of shareable and named elements, incremental access, and a host of access modes, including transactioning facilities. This implementation is built on the concept of a LockBytes object (using the ILockBytes interface) that acts as a device driver for an underlying storage device, shielding the rest of the implementation from the actual device. This makes it easy to redirect bytes to and from mediums other than disks, such as a database record or global memory. Clients access the services provided by OLE's Compound Files through a few OLE API functions, such as StgCreateDocfile and StgOpenStorage, which return IStorage pointers to the root storage objects in the hierarchy. From here, the client can navigate through the rest of the hierarchy, and the root storage object itself also supports IRootStorage for the purposes of low-memory saves.
OLE also provides the ability to associate a CLSID with data in a storage or stream element so that you can use the CLSID to locate an object that can load and work with that data. In addition, registry entries can be used to map a noncompound file to a CLSID based either on the file extension or on byte patterns within the file.
This chapter examines these principles in the context of the Cosmo and Patron samples. Cosmo is converted from using traditional files to using compound files, thus offering a comparison between the two models. Patron is given file capabilities using Compound Files from the beginning and implements a reasonably complex storage mechanism. In addition, a small sample named Fragmenter demonstrates OLE's built-in defragmentation facility to keep compound files to their smallest necessary size.