Structured Storage

Structured storage refers to the hierarchical organization of storage introduced with OLE. In a structured storage environment, storage is organized into two or three types of objects:

Stream and lock bytes are lower-level objects that directly access the data. Stream objects implement the IStream interface which defines methods for reading, writing, positioning, and copying bytes of data. Lock bytes objects implement another OLE interface, ILockBytes, to access data with a byte array. Byte arrays are typically used to provide customized access to underlying storage.

Storage objects are layered on top of the stream or lock bytes objects; they can contain one or more of these objects as well as other storage objects. Storage objects implement the IStorage interface which defines methods for creating, accessing, and maintaining nested objects.

Because IStream, ILockBytes, and IStorage are OLE interfaces rather than MAPI interfaces, their methods return OLE error values rather than MAPI values. Clients and service providers calling methods in these interfaces must use the API function MapStorageSCode to translate these values into MAPI error values.

Clients and service providers use structured storage for working with properties that are too large to maintain with the IMAPIProp methods, typically large string and binary properties. One of the common ways that clients or service providers access them is by specifying IStream or IStorage as the interface identifier in a call to the IMAPIProp::OpenProperty method. For example, clients call OpenProperty with PR_ATTACH_DATA_BIN as the property tag and IID_IStream as the interface identifier to access a binary attachment in a message.

Clients and service providers can implement their own stream and storage objects or call API functions to access implementations supplied by MAPI or OLE. Because the supplied implementations serve most purposes, clients and service providers rarely need to create their own.

When a client calls OpenProperty on a MAPI object to access one of its properties through a storage object, the service provider will typically open the storage object in direct mode. However, this is typical rather than required behavior. Clients should assume that all storage objects opened or created by service providers are transacted and require a call to IStorage::Commit. They should also remember that changes to storage objects will not be made permanent until they call IMAPIProp::SaveChanges after the final Commit to save the MAPI object.

MAPI and OLE provide several API functions for defining or accessing storage and stream objects. The commonly used functions are described in the following table.

Function Description
CreateDocfile Creates a general purpose storage object.
HrIStorageFromStream Creates a storage object to access a stream or lock bytes object.
OpenIMsgOnIStg Creates a message object to access a storage object.
OpenStreamOnFile Creates a stream object to access a file.
WrapCompressedRTFStream Creates a stream object that contains the compressed or uncompressed version of a stream holding the rich text of a message.

CreateDocfile is used frequently in OLE and in MAPI to create a storage object. For more information on this function, refer to the OLE documentation.

    To retrieve the names of the streams in a given substorage
  1. Call the substorage's IStorage::EnumElements method to get an IEnumSTATSTG interface.
  2. Call IEnumSTATSTG::Next with as many STATSTG structures at a time as you can. If you ask for 100 at a time, Next will usually return S_FALSE with the contents of pceltFetched set to the number that were actually retrieved.
  3. Check for the STATSTG structures that are flagged with STGTY_STREAM.
  4. Release the pwcsName parameter.
    To create a storage object to access an existing stream or lock bytes object
    To create a message object to access an existing storage object

OpenStreamOnFile is commonly used for storing file attachments because it creates a stream that reads from and writes to a file. OpenProperty with PR_ATTACH_DATA_BIN as the property tag creates a stream for storing binary attachment data.

    To compress or uncompress a stream containing message text in the Rich Text Format

Note OLE has a storage object implementation based on a byte array that returns an IEnumSTATSTG object from the EnumElements method that is problematic. In particular, the QueryInterface method does not work correctly. Service providers that implement their own storage objects using the OLE implementation should create a thin wrapper for the IEnumSTATSTG object that forwards calls on to the underlying IEnumSTATSTG methods but implements its own AddRef, Release, QueryInterface, and Clone methods.