Implement the Basic Object Class

If you're following through this chapter to add server support to an application, you should start here after you've dealt with the initialization, registry, and class factory issues in steps 1, 2, and 3. Now, having a class factory by itself and no objects to create is useful, for example, only for testing what happens when IClassFactory::CreateInstance fails. You can use this to ensure that your server unloads itself from memory on such an error.

The next step is to begin implementing your embedded object. I say "begin implementing" because implementing the object in pieces is a little less painful than attempting to implement the entire thing at once. So let's start by implementing an object with only the IUnknown interface.

Cosmo's embedded object is a class named CFigure, which is a wrapper for the rest of Cosmo's internals. In other words, CFigure is noninvasive, requiring few changes to most of the other parts of Cosmo. CFigure is defined in COSMOLE.H to implement IOleObject, IDataObject, and IPersistStorage using interface implementation classes as usual. It includes an IStorage pointer and an IStream pointer for the purposes of implementing IPersistStorage, an IDataAdviseHolder variable and FORMATETC arrays for dealing with IDataObject, and IOleAdviseHolder and IOleClientSite pointers for dealing with IOleObject and the container.

The base implementation of CFigure is found in FIGURE.CPP, including its constructor, destructor, IUnknown members, and a few other utility functions. One interesting facet of this implementation is that its Release function does not delete the object itself. When the reference count is 0, it does call the ObjectDestroyed function in COSMO.CPP to start shutdown, but the object itself is deleted in the destructor of CCosmoDoc. This occurs because Cosmo's class factory actually creates a document that in turn creates an instance of CFigure, but the object holds no reference count. Instead, an external reference count is kept. When the figure is released to a zero reference count, it starts server shutdown. This destroys the document and deletes the figure in turn. In this way, the document maintains precise control over the lifetime of CFigure because it needs some of the figure's variables during destruction.

It doesn't necessarily take a zero reference count to start shutdown. If the user closes the document through Cosmo's user interface, the container will still have references to it. In this case, the document is destroyed first. In its destructor, it does the following:


//In CCosmoDoc::~CCosmoDoc
if (NULL!=m_pFigure)
{
m_pFigure->AddRef();
CoDisconnectObject((LPUNKNOWN)m_pFigure, 0L);
m_pFigure->Release(); //Starts shutdown if necessary
delete m_pFigure;
}

The AddRef call on the figure will initially safeguard the object. We then call CoDisconnectObject to remove all external connections. As a result, the figure will have a reference count of 1. When we call Release, the figure will start server shutdown through ObjectDestroyed. The document then deletes the figure and completes its destruction. As shutdown of the server is already under way, the server itself will eventually terminate completely.

This little game that I play inside Cosmo with the CFigure pointer—as opposed to its external reference count—allows me to keep the figure valid until the document is entirely done with it, regardless of whether the user closes the Cosmo document directly or deletes the figure object in the container. In the latter case, server shutdown begins with a zero reference count on the figure object itself. We don't want to delete the figure at this time because the document still expects it to be of value. This is nothing but an internal design choice for Cosmo and has no external bearing on Cosmo's integration with containers.