OLE Documents: Embedding and Linking

Whereas OLE version 1 was about nothing but the creation and management of compound documents—now called OLE Documents—this is now simply a subset of today's OLE. However, many of the other OLE technologies are used to create compound documents.

A compound document is essentially a collection site for unstructured data from a variety of other sources. An important part of this is that the data travels with information about what component was used to create it, along with any information necessary to return it to the exact state it was in when created. The package that works with such data is called a compound document content object, or simply content object. These objects encapsulate any sort of complex information, such as a chart, a table, a graphic, or a sound or video bite, and supply the necessary facilities for editing or otherwise manipulating that content. These objects are stored and managed inside a compound document container that provides the storage and display space for these objects along with the necessary user interface to expose their capabilities. The container, however, is ignorant of the data itself and simply views each piece of content as unstructured.

Unstructured data differs from formatted data (Uniform Data Transfer) and properties (OLE Automation) because the container never touches it directly, nor does it know how to modify or otherwise manipulate that data. That is the responsibility of the object, and the process of activation—whereby a loaded object is made active—makes any sort of editing or manipulation facilities for some content object available to the user in a separate window. Often this form of activation means that the object reappears in the original tool that was used to create it (or an emulating tool). A content object can support more than one way to activate its content as well, with each action called a verb. Relatively static display content, such as a graphic, usually supports a single verb called Edit. Sound and video will typically support a primary Play verb and a secondary Edit verb. The container displays the object's verb as a command in its own menus, and when the user selects a verb the container merely passes it to the object for execution.

How the content object actually gets into the container in the first place can happen in a variety of ways. The user can paste an object from the clipboard or drop one into the container using OLE Drag and Drop. The user can also create a brand-new object using the Insert Object dialog box, as we'll see in Chapter 17.

Regardless of the method used to create the object, there are two ways in which its underlying native data is stored. When the data is encapsulated within the content object itself, that object is said to be embedded in the container. These are also called embedded objects. When the data is actually stored elsewhere, the content object simply maintains a moniker to name that location, and the object is said to be linked to the container, a linked object. A linked object is for the most part identical to an embedded object, with the single exception of where its data is located—the container generally treats both types equally, using the same code.

Programmatically speaking, any such content object will support the IOleObject interface. Although this interface always identifies a content object, it does not supply all the functionality necessary to make compound documents work. The OLE specifications stipulate that all content objects also support IDataObject, IViewObject2, IPersistStorage, and those of the data cache. But don't the IViewObject2 interface and the data cache work only in a client's process? Doesn't IViewObject2 completely lack marshaling support? Does this mean that content objects must always be in-process?

The answer to this last, unrhetorical question is both yes and no, in a way. The complete implementation of a content object can be provided either in-process or out-of-process. However, there must always be at least a partial implementation of the object supplied from an in-process component, usually an in-process handler, and it is this partial object that implements IViewObject2 and caching in order to draw the object in the container. Because of this hard requirement, OLE provides a default handler that provides these services by asking the local object's IDataObject for a metafile or a bitmap that it can then draw in the container. If you want more control over the visual rendering of your object, as well as of any number of other facilities, you can implement your own handler that uses much of the default handler internally through aggregation. In some cases, you might want to implement a complete in-process server, eliminating the need for a handler altogether.

In any compound document relationship, the container always talks to the in-process part of the object first, handler or otherwise. When necessary, the handler will call on a local server to complete the implementation, which is usually required only to retrieve data renderings or to execute verbs. The overall relationship is illustrated in Figure 1-14.

Figure 1-14.

The relationship between a compound document container, an object in a handler, and an object in a local server.

You'll notice in this illustration that the container also implements an object, called the site, for use in a compound document relationship. This site implements IAdviseSink for one, so it receives notifications of data changes, view changes, and a number of other changes in the state of the content object (when it's saved, renamed, closed, and so forth). Alongside this interface, the site provides IOleClientSite simply to provide some information and capabilities of the container to the content object.

All the details regarding embedding and linking, including containers, handlers, and full object implementations, take a number of chapters to cover. Chapters 17 and 18 will cover the basics of embedding for containers and objects from local servers. Chapter 19 will look into object handlers as well as the implementation of a complete in-process content object. Chapters 20 and 21 will then cover the necessary aspects of linking for containers and objects, and in Chapter 21 we'll also see how a container can support linking to objects embedded within it.

As you can imagine, OLE Documents is a fairly lengthy topic, and it is precisely the reason why OLE has the undeserved reputation of being too difficult and too complex. The fact of the matter is that OLE can be very simple on the lower levels, and once you understand those lower-level technologies there isn't too much more you need to do to extend your understanding to OLE Documents. If, however, you try to understand OLE by reading this book starting with Chapter 17, I can guarantee that you'll be confused. A technology as rich as OLE will take some time to understand, so plan your approach carefully and build your understanding from the bottom up.