Low-Memory Save As and IRootStorage

A typical Save operation with a compound file is usually a matter of committing changes and writing memory information into the necessary streams. OLE's implementation of compound files is quite robust under low-memory conditions in that Commit uses no extra memory. Microsoft went to great lengths to make Commit this robust because the only thing an end user really cares about when memory is full is saving data.

Although there's no trouble with a Save operation, a low-memory Save As presents a different problem. When using a transacted compound file, the user's data is split between the original storage contents and a bunch of uncommitted changes being held in a temporary file. To save this information in another storage, the application must call IStorage::CopyTo, which copies the current state of the original storage to the new one, including uncommitted changes. However, CopyTo could not be written to use no extra memory, so there is the possibility that CopyTo will fail, leaving you with a bunch of uncommitted changes to the file you originally opened.

In such a situation, you want to be able to take all uncommitted changes that live in memory and all unchanged parts of the storage that still live on disk and write them all to a new storage without taking up any more memory. In other words, you want to make a copy of the original disk file to the new file and then commit the changes into that new file.

You can do this by using the IRootStorage interface, which OLE provides alongside IStorage on a root storage object (the one connected to the file underneath). You can obtain a pointer to this interface by calling IStorage::QueryInterface, with IID_IRootStorage on a storage object from StgOpenStorage or StgCreateDocfile. IRootStorage has one member function, SwitchToFile(pszNewFile), in which pszNewFile is the name of the new file to associate with the storage object. This function effectively makes a disk copy of the original file and associates your IStorage object internally with that new file. This uses no extra memory and requires no new file handles—hence the reason for the third handle allocated for a transacted compound file. So after SwitchToFile returns, you can call IStorage::Commit to save changes to that new file, again using no extra memory, and perform a successful Save As even under zero memory conditions.