Implement IPersistStorage

We now have an exciting opportunity to start changing specific pieces of each interface to suit our purposes. IPersistStorage is once again the best place to start because it's always called first on an embedded object. As stated before, our reason for implementing this interface is to provide container-side storage handling capabilities. The container can use these to make copies of the object without the local server being present. HCosmo includes a complete implementation of this interface, which also delegates to the default handler for cache handling. Delegation occurs in all the functions as it does in IPersistStorage::InitNew:


STDMETHODIMP CImpIPersistStorage::InitNew(LPSTORAGE pIStorage)
{
if (PSSTATE_UNINIT!=m_psState)
return ResultFromScode(E_UNEXPECTED);

if (NULL==pIStorage)
return ResultFromScode(E_POINTER);

//Good time to initialize our data
m_pObj->m_pl.wVerMaj=VERSIONMAJOR;
m_pObj->m_pl.wVerMin=VERSIONMINOR;
m_pObj->m_pl.cPoints=0;
m_pObj->m_pl.rgbBackground=GetSysColor(COLOR_WINDOW);
m_pObj->m_pl.rgbLine=GetSysColor(COLOR_WINDOWTEXT);
m_pObj->m_pl.iLineStyle=PS_SOLID;

//Make sure these aren't filled with trash.
memcpy(&m_pObj->m_plContent, &m_pObj->m_pl, CBPOLYLINEDATA);
memcpy(&m_pObj->m_plThumbnail, &m_pObj->m_pl, CBPOLYLINEDATA);

m_pObj->m_pDefIPersistStorage->InitNew(pIStorage);

m_psState=PSSTATE_SCRIBBLE;
return NOERROR;
}

Here we're setting up the initial storage with default data, after which we tell the default handler to initialize itself and the cache by calling the same member function through CFigure::m_pDefIPersistStorage. We follow the same procedure with all the other IPersistStorage members but completely ignore the return values. We do this because what really matters is whether the operation succeeded on our own data. The default handler's return values describe whether the operation succeeded on the cache. If the cache fails, big deal—it's just a bonus in the first place. The object's own data is more important.

If a handler implements IViewObject2::Draw for any given aspect in such a way that it never delegates Draw to the default handler, the cache doesn't need to contain a presentation for that aspect. Your handler is entirely responsible for generating presentations for that aspect. If a compound document is taken to another machine, on which not even your handler is present, the object will appear blank in the container. Another good reason to license handlers for free distribution.

A last note about IPersistStorage is a call to OleIsRunning within IPersistStorage::Save:


STDMETHODIMP CImpIPersistStorage::Save(LPSTORAGE pIStorage
, BOOL fSameAsLoad)
{
HRESULT hr;

§

if (OleIsRunning(m_pObj->m_pDefIOleObject))
{
hr=m_pObj->m_pDefIPersistStorage->Save(pIStorage
, fSameAsLoad);

if (SUCCEEDED(hr))
m_psState=PSSTATE_ZOMBIE;

return hr;
}
§
}

OleIsRunning tells us whether the local object connected to this handler is running. If it is, HCosmo completely delegates to the default handler, which saves the cache and calls the local object's IPersistStorage::Save. This ensures that we don't wastefully save our data twice and that what we save in the handler does not conflict with what the local object decides to save. This is especially important if the local object is incrementally accessing the storage and has already written some changes there, changes that a call to Save in the handler might obliterate. We want to avoid conflicts with the running server, so OleIsRunning is just what we need here.