Implement IOleObject::SetMoniker and IOleObject::GetMoniker

The last step for making a complete linking server is to fill in the implementation of IOleObject::SetMoniker and IOleObject::GetMoniker. Implementing SetMoniker is important if you want to support linking to embeddings—that is, when your object doesn't include OLEMISC_CANTLINKINSIDE. This function is not called for one of Cosmo's linked objects but rather for an embedded object that is being linked to inside its container. SetMoniker tells us to register as running the full moniker naming this object in the container:


STDMETHODIMP CImpIOleObject::SetMoniker(DWORD dwWhich
, LPMONIKER pmk)
{
LPMONIKER pmkFull;
HRESULT hr=ResultFromScode(E_FAIL);

if (NULL!=m_pObj->m_pIOleClientSite)
{
hr=m_pObj->m_pIOleClientSite->GetMoniker
(OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_OBJFULL
, &pmkFull);
}

if (SUCCEEDED(hr))
{
if (NOERROR==pmkFull->IsRunning(NULL, NULL, NULL))
{
pmkFull->Release();
return NOERROR;
}

//This will revoke the old moniker if m_dwRegROT is nonzero.
INOLE_RegisterAsRunning(m_pObj, pmkFull
, 0, &m_pObj->m_dwRegROT);

//Inform clients of new moniker.
if (NULL!=m_pObj->m_pIOleAdviseHolder)
m_pObj->m_pIOleAdviseHolder->SendOnRename(pmkFull);

pmkFull->Release();
}

return hr;
}

This code works in conjunction with our call to INOLE_NoteChangeTime earlier. Cosmo maintains two registration keys (m_dwRegROT) from IRunningObjectTable::Register: one in CCosmoDoc and the other in CFigure. The former is used when Comso is handling a linked object. In that case, the variable in CFigure has a copy of the variable in CCosmoDoc. If we're embedded and SetMoniker is called, the document variable is 0 and the CFigure variable is the one from the registration shown in the preceding code. In either case, we'll always call IRunningObjectTable::NoteChangeTime for the right moniker in the right circumstances.

IOleObject::GetMoniker is used when Cosmo is servicing a linked or an embedded object, and it returns the full moniker to the object in either case. For linking, this is the document moniker; for embedding, this is the moniker we can get from the container:


STDMETHODIMP CImpIOleObject::GetMoniker(DWORD dwAssign
, DWORD dwWhich, LPMONIKER *ppmk)
{
HRESULT hr=ResultFromScode(E_FAIL);

*ppmk=NULL;

if (NULL!=m_pObj->m_pMoniker)
{
*ppmk=m_pObj->m_pMoniker; //Document file moniker
m_pObj->m_pMoniker->AddRef();
}
else
{
//Get full container:object moniker if we're embedded.
if (NULL!=m_pObj->m_pIOleClientSite)
{
hr=m_pObj->m_pIOleClientSite->GetMoniker
(OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_OBJFULL
, ppmk);
}
}

return (NULL!=*ppmk) ? NOERROR : hr;
}

And with that, we have completed the implementation of a simple link-source server.


Closing a Linked Document Without Saving

When a user activates a linked object in a container, changes made to the object in the server are immediately reflected in the object image in the container site. Users usually save their file and then close the server when they have finished making changes. It is possible that a user can close the file and not save changes, in which case the image of the object in the container does not reflect what is actually in the source data. This is in fact the expected behavior of such linked objects, and it is a very difficult problem to handle in another way. It requires the server to reload its original data and update clients again before closing, which might not be all that easy. Furthermore, the same problem exists when links are broken—that is, when the site image in the container does not match the actual source data. So the container—and the cache in OLE—try to maintain an updated presentation within reasonable bounds. To deal with this, a container can try to detect this situation and revert its cache to some previous state, but that too is difficult. This is one update problem that simply doesn't have a good solution today.