Linking to Embeddings

Consider a typical word processor with an open report containing an embedded table with the latest sales figures, as shown in Figure 21-4.

Figure 21-4.

A typical word processor document that contains an embedded table object.

Because the table is an embedded object, its data exists only in that embedded object. If the table were linked, its data would exist in another source. In this case, however, the table is the sole source for that data. Now suppose we want to add a chart to the report to show the numbers in that table graphically. We could create a new chart object and manually reenter all the numbers in the table to fill out the chart, but that would be tedious and detrimental to our quest for enlightenment. What we would really like to do is use the embedded table as a link source for the chart, as shown in Figure 21-5.

Figure 21-5.

Linking a chart to an embedded table. In this example, the table is the only source for the data.

This demonstrates linking to embeddings because embedded objects are valid sources of data as much as any other source outside the compound document. To make this work, the container itself has to provide link-source data when copying the embedded object to the clipboard or in a drag-and-drop operation. That means that the container must provide a moniker to name the embedded object inside the compound document, just as a linking server would name a pseudo-object in its own file. Keep in mind that the embedded object must support this sort of linking. The bit OLEMISC_CANTLINKINSIDE tells the container that such support is not present.

If the container can create a moniker to name the embedded object, any other container—even the original container itself—can now create a link to that embedded object by using the moniker (obtained from a Paste or a drop operation). Creating a chart that is linked to a table, for example, might require a sequence of steps that involve the creation of a new chart object followed by a call to its IOleObject::InitFromData function so that it can obtain the link-source data. In one way or another, the chart object can now obtain an IDataObject pointer for the table object. Through this pointer, the chart can extract the necessary data to render its own presentations.

Of course, the linked object doesn't always end up in the original container, but that linked object must still support activation. In this case, activation means that we must launch the original container and have it load and activate the embedded object. Thus, the container needs to support all the binding mechanisms necessary for whatever moniker it uses to name the embedded object. This includes a class factory, an IPersistFile implementation, and any number of IOleItemContainer implementations. But the container doesn't implement the object itself—when asked for an interface pointer, it returns one from the embedded object.

We will demonstrate linking to embeddings with this chapter's version of Patron, which provides a File!Item!Item moniker to name an embedded object in a certain page of a certain document. Accordingly, Patron has to support binding for this moniker through the structure shown in Figure 21-6. Obviously, this will add some complexity to the application, but most of it is the result of moniker binding support.

Figure 21-6.

The structure of Patron as a source for links to embedded objects.

Linking to embeddings does make one demand on in-process servers for embedded objects. To show this, we'll update Polyline. First Polyline has to implement the moniker-related functions in IOleObject. It must then register itself as running within IRunnableObject::Run, using the container's name for embedding as obtained from the container's IOleClientSite::GetMoniker(OLEWHICHMK_OBJFULL). At this time, the in-process object must also call the container's IOleContainer::LockContainer with TRUE. Now it is up to the in-process object to start a shutdown of the container in which it lives when a remote link to the object is released. To do so, the object implements IExternalConnection and calls its own IOleObject::Close function within IExternalConnection::RemoveConnection. IOleObject::Close then calls IOleContainer::LockContainer(FALSE), which causes the container to start its own shutdown. Through all of this, the other linking container can silently retrieve an update of the embedded object through the intermediate container, ensuring that everything is brought into and taken out of memory as necessary.