Another simple persistence model for an object is for it to read and write its information from a separate file, be it a traditional file or a compound file. Components that support linking through a file moniker (as many do when supporting linked objects with OLE Documents) will implement this interface. Through it a client can ask a component to load or save a file that might contain other instances of object data of interest to that client. As we'll see in practice in Chapter 9, when we deal with file moniker binding, the IPersistFile interface describes load and save semantics for a separate file, illustrated in the following:
interface IPersistFile : IPersist
{
//IUnknown members and GetClassID from IPersist
HRESULT IsDirty(void);
HRESULT Load(LPCOLESTR pszFile, DWORD dwMode);
HRESULT Save(LPCOLESTR pszFile, BOOL fRemember);
HRESULT SaveCompleted(LPCOLESTR pszFile);
HRESULT GetCurFile(LPCOLESTR *ppszFile);
};
These functions behave as follows:
Member | Description |
IsDirty | Indicates whether the object considers itself dirty. If the object is dirty, the client should call Save before releasing the object, as with IPersistStream[Init]. |
Load | Instructs the object to load its persistent data from the file with the given filename, using the given access flags. The object is allowed to keep this file open, and if the file is a compound file, the object can open and hold pointers to any storage and stream elements for incremental access. |
Save | Instructs the object to save its persistent data in the file of the given name. If pszFile is NULL, this works like a File Save operation: the object saves data into the file opened in Load. Otherwise, the call is equivalent to Save As or Save Copy As, where the fRemember flag distinguishes the behavior: TRUE saves the file under a new name, which becomes the current file for the object; FALSE saves a copy but doesn't change the current file. For any operation other than the Save Copy As case, Save must clear the object's dirty flag (as returned from IsDirty) and release any open pointers or handles to the file. See also SaveCompleted. |
SaveCompleted | Informs the object that the calling client has completed its overall save procedure and that the object can reopen files and storage or stream elements. The relationship of Save and SaveCompleted allows a client to manipulate the file itself between the two calls without having to worry about sharing violations that might arise because the object has kept the file open. If the pszFile argument is NULL, the object reopens its known file; if it is non-NULL, the object opens the named file as the current file. |
GetCurFile | Returns a copy of the object's current absolute pathname, allocated with the task memory allocator (CoTaskMemAlloc). The caller becomes responsible for the memory. |
As mentioned earlier, IPersistFile can act as an initialization interface, with Load being the only member available for this purpose.
The relationship between Save and SaveCompleted deserves a little more clarification. After a persistent object is initialized through IPersistFile::Load, it is allowed to scribble into that file—that is, read and write incrementally as it sees fit. If the object wants to scribble, it can keep a file open and hold as many handles or pointers as it needs. When the object is told to save (in the current file or a new current file), it must write its current state and turn off this scribbling mode until SaveCompleted is called. Turning off scribbling means releasing any open handles or pointers that the object can reopen only during SaveCompleted. In this way, a client can avoid sharing violations or other access problems with that file, especially if the client needs to move that file or rename it. The pszFile argument to SaveCompleted is the way the client identifies the file that the object can reopen; a NULL means "current file," and a non-NULL name can identify the same file that has been moved to another location or a different file altogether.
Keep in mind that if the client calls Save(pszFile, FALSE), it is simply making a new copy of the data in another file and will always call SaveCompleted(NULL) afterward. In this case, the object need not close the original file.