Summary

A local server for embedded content objects is primarily responsible for defining what an object looks like, how it behaves, and what sort of native data is required to generate and regenerate that content object whenever necessary. The structure of a content object in a local server is considerably simpler than the structure of an in-process handler. Specifically, such an object needs to implement only IPersistStorage, IDataObject, and IOleObject.

IPersistStorage gives the object the ability to incrementally access its native data in persistent storage, that is, inside the container's compound document itself. The object does not need to load any data until absolutely necessary, and it can also write incremental changes. When an object wants to save itself, it actually asks the container to do so first by calling IOleClientSite::SaveObject, which gives the container a chance to save the cache and any other information the container maintains for that object. In turn, the container will tell the object to save its own data by calling IPersistStorage::Save. It is also through IPersistStorage that a server supports conversion as well as emulation—the server's implementation of this interface must be able to read and possibly write foreign formats in order to support the conversion and emulation features.

IDataObject is how the cache on the container side of the picture retrieves graphical presentations from the object for whatever display aspect is used in the container. Correspondingly, a content object in a local server must supply at least CF_METAFILEPICT, CF_BITMAP, or CF_DIB through IDataObject::GetData. Any other formats are optional but usually include CFSTR_EMBEDSOURCE or CFSTR_EMBEDDEDOBJECT.

IOleObject is the workhorse of OLE Documents, but of its 21 member functions, only 3 really require nontrivial implementations. The most notable is IOleObject::DoVerb, which instructs an object to activate. Activation is, of course, one of the key elements of OLE Documents as a technology. The other most important member function is IOleObject::Close, which tells an object to move from the active or running state to the loaded state. Other member functions can be left unimplemented, delegated to helper functions in OLE, or implemented by returning a value that tells OLE to provide a default implementation. So overall, even this large interface doesn't present a major problem to the implementation of a local server for OLE Documents.

Besides the differences between local and in-process servers, there is also a difference between miniservers and full servers. The former are typically written for the express purpose of servicing an embedded object; that is, the user cannot run a miniserver as a stand-alone server but only to create a new object or to manipulate an existing one. As a consequence, miniservers typically do not support linking. Full servers, on the other hand, can run as stand-alone servers, outside OLE Documents altogether, and can easily support linking.

This chapter takes a brief look at the structure of local servers and the considerations that influence uses of miniservers and full servers. The implementation of a local server for embedded content objects is detailed in step-by-step fashion, using the Cosmo sample for demonstration. The treatment of in-process servers for OLE Documents is left to Chapter 19.