IOleLink and the Links Dialog

The IOleLink interface is the essence of linked objects. It defines what a linked object can do differently from an embedded object.


interface IOleLink : IUnknown
{
HRESULT SetUpdateOptions(DWORD dwUpdateOpt);
HRESULT GetUpdateOptions(DWORD *pdwUpdateOpt);

HRESULT SetSourceMoniker(IMoniker *pmk, REFCLSID rclsid);
HRESULT GetSourceMoniker(IMoniker **ppmk);

HRESULT SetSourceDisplayName(LPCOLESTR pszStatusText);
HRESULT GetSourceDisplayName(LPOLESTR *ppszDisplayName);

HRESULT BindToSource(DWORD bindflags, IBindCtx *pbc);
HRESULT BindIfRunning(void);

HRESULT GetBoundSource(IUnknown **ppunk);
HRESULT UnbindSource(void);
HRESULT Update(IBindCtx *pbc);
};

With what you know about monikers, you can probably guess what half of these functions do. Both SetSourceMoniker and GetSourceMoniker allow the container to change the link, which is necessary if the link is broken. SetSourceDisplayName and GetSourceDisplayName are more or less wrappers for MkParseDisplayName and IMoniker::GetDisplayName. BindToSource is a wrapper forIMoniker::BindToObject, and BindIfRunning first checks the running object table and binds only if the moniker is already there. GetBoundSource extracts a running object's pointer from the running object table, and UnbindSource simply disconnects the linked object from a running object to stop the flow of automatic updates.

SetUpdateOptions and GetUpdateOptions deal with the flags OLEUPDATE_ALWAYS and OLEUPDATE_ONCALL, which are mutually exclusive. OLEUPDATE_ALWAYS means that the link is an automatic or hot link, a link for which changes in the source are reflected in the linked object's presentation in the container. In other words, the linked object will actively send IAdviseSink::OnViewChange notifications to the container when this option is set. OLEUPDATE_ONCALL makes a manual or warm link. Here the user must manually ask to update the linked object's presentation. This is exactly what IOleLink::Update is for.

Almost all code that calls this interface is concentrated in the standard Links dialog, shown in Figure 20-4. Using this dialog, the user can view the links in the compound document, modify the update options, force an update, run the server and have it open the linked data, change the moniker (by entering a new display name in a File Open type of dialog), or even break the link entirely, converting the linked object to a static one.

Figure 20-4.

The standard Links dialog box provided by the OLE UI Library.

The majority of a container's linking support is found in its support for this dialog. What isn't there, besides linked object creation and the Show Objects command, are two other conveniences for the end user. First, when opening a compound document, the container should update, if necessary, all linked objects marked OLEUPDATE_ALWAYS, displaying the Links dialog box if one or more of those links cannot be updated. The second convenience is that the container should ensure that all linked objects are automatically reconnected to their running sources, if possible, by calling IOleLink::BindIfRunning when loading those linked objects.

I said "update, if necessary," so how does OLE or a container determine whether a linked object is actually up-to-date with its source? This is probably the hardest problem to solve as far as linking is concerned. Also, with today's operating systems, in which we have at best file time stamps with which to guess when data might have changed, the solution is imperfect. Determining whether a linked object is up-to-date can be as expensive as simply updating it anyway. It is often necessary to run the source server itself and ask it about the update status.

That aside, most of the container's handling of a linked object is otherwise identical to its handling of an embedded object. Linked objects have verbs that are the same as any others, and activating the object results in the same thing. Activation, however, occurs through moniker binding; specifically, the default link handler calls the moniker's BindToObject and asks for IOleObject. Through this interface, it can then ask the object to execute a verb. If for some reason the link is broken, however, the binding process will fail, and the user has to repair the link.