We saw in Chapter 17 how the data cache maintains a presentation cache in the container's storage. Somehow, however, that cache must obtain presentations to store in itself. For that reason, a content object in a local server must implement IDataObject, through which the cache can retrieve rendering in at least the CF_METAFILEPICT format (TYMED_MFPICT) and preferably in CF_DIB (or CF_BITMAP) as well. Technically speaking, implementing IDataObject::GetData for one of these formats is the absolute bare minimum support an object must have for OLE Documents.
An object will, however, also want to support its native data format through both GetData and SetData. A container that knows more about your object might ask for your object's data or might give your object some data to integrate into it. If you don't foresee a reason why someone would want to use GetData or SetData for your private format, you have no reason to support such functionality in IDataObject. One case in which you must support GetData on your native format occurs when you have your own object handler. The object handler can then synchronize its data with that of the local object. (We'll cover this in Chapter 19.)
It is best to also include support for CFSTR_EMBEDSOURCE through both GetData and GetDataHere. In the former, you create a new storage object and save the object's data to it, using your own IPersistStorage::Save if you want. In the latter case, the container has provided a storage object already, so you can save the object directly to it. Either way, you should write your object's exact persistent representation to the storage. A container can use this data to create a new copy of the object, and the storage is passed that new instance through IPersistStorage::Load. You really do want to be sure it's the same data.
Here's how Cosmo's figure handles all supported formats through GetData and GetDataHere:
STDMETHODIMP CImpIDataObject::GetData(LPFORMATETC pFE
, LPSTGMEDIUM pSTM)
{
UINT cf=pFE->cfFormat;
BOOL fRet=FALSE;
//Another part of us already knows whether the format is good.
if (NOERROR!=QueryGetData(pFE))
return ResultFromScode(DATA_E_FORMATETC);
if (CF_METAFILEPICT==cf œœ CF_BITMAP==cf œœ m_pObj->m_cf==cf)
{
if (CF_METAFILEPICT==cf)
{
pSTM->tymed=TYMED_MFPICT;
}
else
pSTM->tymed=TYMED_HGLOBAL;
pSTM->pUnkForRelease=NULL;
pSTM->hGlobal=m_pObj->m_pDoc->RenderFormat(cf);
fRet=(NULL!=pSTM->hGlobal);
}
else
fRet=m_pObj->m_pDoc->RenderMedium(cf, pSTM);
return fRet ? NOERROR : ResultFromScode(DATA_E_FORMATETC);
}
STDMETHODIMP CImpIDataObject::GetDataHere(LPFORMATETC pFE
, LPSTGMEDIUM pSTM)
{
UINT cf;
LONG lRet;
cf=RegisterClipboardFormat(CFSTR_EMBEDSOURCE);
//Aspect is unimportant to us here, as are lindex and ptd.
if (cf==pFE->cfFormat && (TYMED_ISTORAGE & pFE->tymed))
{
//We have an IStorage we can write to.
pSTM->tymed=TYMED_ISTORAGE;
pSTM->pUnkForRelease=NULL;
lRet=m_pObj->m_pPL->WriteToStorage(pSTM->pstg
, VERSIONCURRENT);
if (lRet >= 0)
return NOERROR;
return ResultFromScode(STG_E_WRITEFAULT);
}
return ResultFromScode(DATA_E_FORMATETC);
}
GetDataHere is implemented entirely by calling CPolyline::WriteToStorage—that's all there is to it. In GetData, we use the function CPatronDoc::RenderFormat to generate the necessary rendering. The document turns around and uses code in CPolyline to fulfill the request because the Polyline object already knows how to copy its data into global memory and how to draw itself into metafiles and bitmaps. We can see this in the code on the following page.
HGLOBAL CCosmoDoc::RenderFormat(UINT cf)
{
HGLOBAL hMem;
if (cf==m_cf)
{
m_pPL->DataGetMem(VERSIONCURRENT, &hMem);
return hMem;
}
switch (cf)
{
case CF_METAFILEPICT:
return m_pPL->RenderMetafilePict();
case CF_BITMAP:
return (HGLOBAL)m_pPL->RenderBitmap();
}
return NULL;
}
This is another example of how an embedded object implementation can be built on top of existing code. The implementation of SetData is much the same because it passes the data on to CPolyline::DataSetMem.
The other member functions have trivial implementations. GetCanonicalFormatEtc is the standard "all formats are the same" implementation that we saw in Chapter 10. EnumFormatEtc is implemented as follows:
STDMETHODIMP CImpIDataObject::EnumFormatEtc(DWORD dwDir
, LPENUMFORMATETC *ppEnum)
{
return ResultFromScode(OLE_S_USEREG);
}
Because this interface is implemented on a local content object, it is always some part of OLE that is calling us, never the container directly. By returning OLE_S_USEREG, we tell OLE to implement this function for us using our own registry entries. In other words, OLE calls OleRegEnumFormatEtc on our behalf. Things are so easy around here!
Finally, we implement the three amigos—DAdvise, DUnadvise, and EnumDAdvise—using an OLE-provided data advise holder. The advise holder is created in DAdvise with CreateDataAdviseHolder. All of these members simply delegate to the advise holder, as we've seen before.