Notes on Implementation of Cosmo
As mentioned earlier, the Cosmo sample in this chapter implements a three-level automation object hierarchy: application–figures (collection)–figure. Rather than labor over the code here, take a look at the list beginning on the following page, which details the major points of interest in the implementation. You can look through the source code for all the details.
- Cosmo's type information is provided only in English in COSMO000.ODL, although the structure of the code would allow a multilingual version quite easily.
- The automation objects are implemented by C++ classes named CAutoApp (AUTOAPP.CPP), CAutoFigures (AUTOFIGS.CPP), and CAutoFig (AUTOFIG.CPP). These classes all inherit from the common abstract base class CAutoBase (AUTOBASE.CPP), which provides the IDispatch and IExternalConnection implementations but forces the derived class to implement IUnknown members. IDispatch is implemented through the technique shown in Beeper2 using ITypeInfo::Invoke and other functions; Cosmo does not raise exceptions.
- Each automation object is attached to one of Cosmo's existing framework classes: CAutoApp to CCosmoFrame, CAutoFigures to CCosmoClient, and CAutoFigure to CCosmoDocument. CAutoBase maintains a backpointer from the automation object to the framework object so that the automation implementation can call the various framework member functions to implement the correct automation behavior.
- Cosmo is registered as the server for two CLSIDs: CLSID_CosmoApplication and CLSID_CosmoFigure. The registry entries for the application object include the /Automation switch. This switch tells CCosmoFrame::Init (COSMO.CPP) whether to register a class factory for CLSID_CosmoApplication on startup: only if the switch is present does a single-use registration happen because that's the only instance in which anyone should ever have access to the class factory. If /Automation is not present but /Embedding is, Cosmo was launched to serve an instance of CLSID_CosmoFigure. In this case, Cosmo's main window is hidden until told otherwise (through CAutoApp::put_Visible if the caller navigates up through CAutoFigure::get_Application). Otherwise, Cosmo is being launched as a stand-alone. In any launch scenario, Cosmo always registers a multiple-use class factory for CLSID_CosmoFigure.
- Regardless of how Cosmo is launched, it always instantiates CAutoApp on startup and registers it as active (that is, running) with RegisterActiveObject. Cosmo removes this registration during shutdown with RevokeActiveObject. To control reference-counting problems with active-object registration, Cosmo registers using a strong reference and exposes IExternalConnection through CAutoApp. (See AUTOBASE.CPP.) This interface simply calls CAutoApp::Release when the last external connection goes away. This reverses the AddRef from RegisterActiveObject and starts shutdown. CAutoApp is not destroyed until the CCosmoFrame destructor is called, so RevokeActiveObject will have no problem calling Release itself.
- A document always instantiates CAutoFigure on creation and deletes it on closure. This automation object is also registered with a strong lock with RegisterActiveObject and uses the same IExternalConnection device to close the document when necessary (just as CAutoApp uses it). If Cosmo was launched to create a figure object, the communication from CAutoFigure::Release to the ObjectDestroyed function in COSMO.CPP initiates full shutdown of the application when the last OLE-controlled document is closed.
- Both CAutoApp and CAutoFigure have an additional private member function named MoveSize. This function handles all the variations with the Left, Top, Width, and Height properties.
- CAutoFigures uses a list box of current documents maintained in CCosmoClient (through its base class CClient in CLASSLIB) to maintain the documents in the collection. The class CEnumFigures (AUTOFIGS.CPP) handles enumerations using the copy of this list that it makes when the enumerator is instantiated from CAutoFigures::_NewEnum.
- CAutoFigures::Item gives a good example of handling multiple optional arguments because it accepts either no arguments, a long index, or a BSTR name of the document to return. CAutoFigure::Close is another example of optional arguments.
- CAutoFigures::AddPoint, which adds another point to the figure and repaints it, is implemented by converting the (32,767, 32,767) scaled point to the size of the figure window itself. It then sends the figure (class CPolyline, in POLYLINE.CPP and POLYWIN.CPP) a WM_LBUTTONDOWN message with the scaled coordinates in lParam. This simulates a user's mouse click exactly, in response to which the figure adds the point to its data and repaints.
- The only code changes to Cosmo's framework classes that were necessary to support Automation concerned object creation, management, and shutdown. No changes were necessary to implement the member functions of any automation interface. Again, this is utterly noninvasive, made possible by the fact that Cosmo's framework was already structured in a parallel fashion with the automation object hierarchy it exposes.
- For a non-debug Win32 compilation, the executable increased from 68,608 bytes to 84,432 bytes, an increase of 23 percent. This is roughly the amount of code added to support Automation, but the larger an application and the more complex its functionality, the smaller this percentage will be. In Cosmo, the non-OLE functionality is so simple that another set of objects to access that functionality is a significant addition. If Cosmo were much more complex in terms of internal computation, for example, the percentage would be quite a bit smaller. This measure of Cosmo is possibly the worst- case scenario for code expansion.
If you want to give Cosmo a whirl, you can use test programs in the COSMOTST directory.