Mommy, Daddy, Where Do New Content Objects Come From?

Um, well, ah, you see, there's, ah, a stork. Yeah. The Object Stork.

Sure, an explanation like this might work for a two-year-old, but I don't think it works for programmers—there must be a better explanation. How does a container create a new content object or obtain a copy of one? It is not simply a matter of calling CoCreateInstance with a CLSID because with persistent storage, a cache, and related matters, there are additional initialization concerns. For this reason, OLE provides a number of API functions for creating or loading content objects.

The most fundamental of these functions is OleCreate, which is the equivalent of CoCreateInstance for embedded objects. This function will create the object (using CoCreateInstance internally), initialize it through IPersistStorage::InitNew, send the object the container's IOleClientSite pointer if it's wanted, and initialize the cache. OleCreate takes an object from a nonexistent state into a loaded state, from which the container can run and activate it as necessary. Closely related to this is OleCreateFromFile, which uses the information in a file to initialize the new embedded object (which itself must implement IPersistFile as well to support this ability).2 If a server for the file type is not available, OleCreateFromFile creates what is called a Package object, which maintains a copy of the file as its native data. Activating a package object causes it to write that file to a temporary location and try to run it as if the user double-clicked that file in the system shell.

A container can also paste an embedded object from the clipboard or by using drag and drop. In both cases, the container will have an IDataObject pointer that encapsulates the embedded object's data formats. OLE defines several formats for this purpose. CFSTR_EMBEDDEDOBJECT and CFSTR_EMBEDSOURCE, whose formats are registered using the strings "Embedded Object" and "Embed Source", are both copies of the object's persistent storage inside a separate storage element (that is, TYMED_STORAGE).

The presence of either format in a data object means that an embedded object is available. The function OleQueryCreateFromData checks for this availability, and OleCreateFromData creates an embedded object from that data. Afterward, the object is no different from what is created through OleCreate. Another data format, CFSTR_OBJECTDESCRIPTOR (the string "Object Descriptor") usually travels along with the embedded object to provide more information about the data—for example, its extents and its pick point if the data is used in drag and drop.

Linked objects have several functions analogous to the ones for embedded objects: OleCreateLink, OleCreateLinkToFile, OleCreateLinkFromData, and OleQueryCreateLinkFromData. The latter two deal with data of the format CFSTR_LINKSOURCE ("Link Source") and CFSTR_LINKSRCDESCRIPTOR ("Link Source Descriptor").

When any linked or embedded object exists in its passive state, the OleLoad function brings it to the loaded state and returns an interface pointer, performing all of the necessary initialization steps done in OleCreate and others. OleLoad will always work, even if no object-specific code exists. In that case, OLE creates an instance of the default handler. Because the handler works with the data cache, the container can always view and print the object using the cached presentations. Activating the object, however, will not work.


Containers/Servers and Embedding in Yourself

Many high-end business applications such as word processors and spreadsheets are the sorts of applications that can act both as compound document containers and as servers for material such as text, tables, charts, and so forth. This brings up an interesting possibility: the application can register itself as Insertable so that its own name will appear in its own Insert Object dialog box. This creates the possibility of inserting an object into its own application, called a container/server. Each half of the application, however, should still be able to work with the other half as if the object or the container came from a separate application. It doesn't make any sense to run another instance of the application under any circumstances, so using the default handler in such a situation is risky and wasteful. For this reason, OLE provides a hobbled handler called the embedding helper, created through the API function OleCreateEmbeddingHelper, which provides an efficient mediator to facilitate a container/server's own communication with itself through the standard protocol of OLE Documents. For further information, see the OLE Programmer's Reference.


2 The version of Cosmo in Chapter 18 does not implement this support and will not work with OleCreateFromFile. The version of Cosmo in Chapter 21 does include the necessary support.