COMUSER - User of COM Objects in COMOBJ.DLL


SUMMARY
=======

The COMUSER sample presents a series of COM objects, several of which were
introduced in the COMOBJ code sample. These objects represent various types
of cars, and they expose the following interfaces: ICar, IUtility, and
ICruise. COMUSER works in conjunction with the separate COMOBJ.DLL, which
creates COCar, COUtilityCar, and COCruiseCar COM objects.

COMUSER.EXE creates its own COUtilityCruiseCar COM object, which is
constructed by reusing the COCruiseCar COM object by aggregation and
augmenting it with the IUtility interface. Because the COCruiseCar COM
object class is also an aggregate--that is, it reuses an inner COCar object
by aggregation--COMUSER illustrates nested aggregation of COM objects.

For functional descriptions and a tutorial code tour of COMUSER, see the
Code Tour section below. See also COMOBJ.TXT in the sibling COMOBJ directory
for more details on how COMOBJ works and exposes its services to COMUSER.
You must build COMOBJ.DLL before building COMUSER. The makefile for COMOBJ
copies the necessary COMOBJ.H, COMOBJ.LIB, and COMOBJ.DLL files to the
appropriate sibling directories once the files are built. For details on
the external user operation of COMUSER, see the Operation section below.

In general, to set up your system to build and test the code samples in this
OLE Tutorial series, see the global TUTORIAL.TXT file for details. The
supplied makefile is Microsoft NMAKE-compatible. To create a debug build,
issue the NMAKE command in the Command Prompt window.

Usage
-----

COMUSER is a simple application that you can execute directly from Windows
in the normal manner or from the command prompt. No command line parameters
are recognized by COMUSER.


OPERATION
=========

The COMUSER.EXE application provides the main user interface for this
lesson. It exercises the associated, but independent, COMOBJ.DLL. Here is
a summary of operation from the standpoint of COMUSER.EXE as a controller
of COMOBJ.DLL.

The COM objects that are used in the COMUSER and COMOBJ code samples
represent sport utility vehicles. We invent some basic feature sets for
modeling such car objects. These feature sets are implemented as
interfaces to COM objects. The ICar interface provides some basic car
behavior: Shift, Clutch, Speed, and Steer. The IUtility interface provides
off-road utility systems: Offroad and Winch. The ICruise interface
provides automatic cruise control facilities: Engage and Adjust.

COMUSER.EXE provides menus for creating, releasing, and invoking methods
for four COM objects: COCar, COUtilityCar, COCruiseCar, and
COUtilityCruiseCar. These objects have combinations of the ICar,
IUtility, and ICruise interfaces. COCar objects expose the ICar
interface. COUtilityCar objects expose the ICar and IUtility interfaces.
COCruiseCar objects expose the ICar and ICruise interfaces.
COUtilityCruiseCar objects expose the ICar, ICruise, and IUtility
interfaces. As a result, COCar objects have only the basic car behavior
(ICar). COUtilityCar objects have basic car behavior (ICar) with sport
utility systems (IUtility). COCruiseCar objects have basic car behavior
(ICar) with an automatic cruise control system (ICruise).
COUtilityCruiseCar objects have basic car behavior (ICar), a cruise
control system (ICruise), and a sport utility system (IUtility).

COCar is constructed as an aggregatable COM object with a native
implementation of the ICar interface. COUtilityCar is constructed using
containment and is implemented in COMOBJ.DLL. For details, see the COMOBJ
lesson. COCruiseCar is constructed using aggregation and is also
implemented in COMOBJ.DLL. COUtilityCruiseCar is constructed using
aggregation and is implemented in COMUSER.EXE. COUtilityCruiseCar
aggregates COCruiseCar, so nested aggregation is thereby illustrated in
this lesson.

COMUSER.EXE presents a menu for each of these four main COM objects.  Each
menu has items that call the methods of the various available interfaces.
The code samples (both COMUSER and COMOBJ) have trace message log
statements throughout. When you exercise the objects from COMUSER.EXE, the
main COMUSER window will display a log of internal activity in these COM
objects.

Menu Selection: File/Exit
Exits COMUSER.

Menu Selection: Car/Create
Creates a COCar COM object. A checkmark beside the menu item indicates
that there is already an instance of the object.

Menu Selection: Car/Release
Releases the COCar COM object.

Menu Selection: Car/ICar::Shift
Calls the ICar::Shift method on the COCar object.

Menu Selection: Car/ICar::Clutch
Calls the ICar::Clutch method on the COCar object.

Menu Selection: Car/ICar::Speed
Calls the ICar::Speed method on the COCar object.

Menu Selection: Car/ICar::Steer
Calls the ICar::Steer method on the COCar object.

Menu Selection: UtilityCar/Create
Creates the COUtilityCar COM object. A checkmark beside the menu item
indicates that there is already an instance of the object.

Menu Selection: UtilityCar/Release
Releases the COUtilityCar COM object.

Menu Selection: UtilityCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCar object.

Menu Selection: UtilityCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCar object.

Menu Selection: UtilityCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCar object.

Menu Selection: CruiseCar/Create
Creates the COCruiseCar COM object. A checkmark beside the menu item
indicates that there is already an instance of the object.

Menu Selection: CruiseCar/Release
Releases the COCruiseCar COM object.

Menu Selection: CruiseCar/ICar::Shift
Calls the ICar::Shift method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Speed
Calls the ICar::Speed method on the COCruiseCar object.

Menu Selection: CruiseCar/ICar::Steer
Calls the ICar::Steer method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COCruiseCar object.

Menu Selection: CruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COCruiseCar object.

Menu Selection: UtilityCruiseCar/Create
Creates the COUtilityCruiseCar COM object. A checkmark beside the menu
item indicates that there is already an instance of the object.

Menu Selection: UtilityCruiseCar/Release
Releases the COUtilityCruiseCar COM object.

Menu Selection: UtilityCruiseCar/ICar::Shift
Calls the ICar::Shift method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Clutch
Calls the ICar::Clutch method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Speed
Calls the ICar::Speed method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICar::Steer
Calls the ICar::Steer method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Engage
Calls the ICruise::Engage method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/ICruise::Adjust
Calls the ICruise::Adjust method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Offroad
Calls the IUtility::Offroad method on the COUtilityCruiseCar object.

Menu Selection: UtilityCruiseCar/IUtility::Winch
Calls the IUtility::Winch method on the COUtilityCruiseCar object.

Menu Selection: Log/Clear
Clears the trace message log display.

Menu Selection: Log/Logging
Toggles the trace message logging facility on or off. A checkmark beside
the menu item indicates that logging is on. Logging can be engaged but
simply turned on or off. Unchecking this command turns the trace message
logging facility off but does not disengage the logging mechanisms.

Menu Selection: Log/Copy
Copies the current contents of the trace message log to the Windows
Clipboard.

Menu Selection: Help/Read COMUSER.TXT
Opens the COMUSER.TXT file (this file) in the Windows Notepad.

Menu Selection: Help/Read COMOBJ.TXT
Opens the COMOBJ.TXT file from the sibling \COMOBJ directory in the Windows
Notepad.

Menu Selection: Help/Read Source File
Displays the Open common dialog box so you can open a source file from this
lesson or another one in the Windows Notepad.

Menu Selection: Help/About COMUSER
Displays the About dialog box for this application, a standard part of
this series of code samples. The code illustrates how to program the use
of the CAboutBox class provided by APPUTIL.LIB.

Menu Selection: Help/About COMOBJ
Displays the About dialog box for COMOBJ.DLL, which is used by this
application.  In this series of code samples, partner DLLs like COMOBJ are
given their own About dialog box in the native resources of the DLL. This
menu item calls the DLL function that displays this dialog box.


CODE TOUR
=========

Files          Description

COMUSER.TXT    This file.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
COMUSER.H      The include file for the COMUSER application. Contains
               class declarations, function prototypes, and resource
               identifiers.
COMUSER.CPP    The main implementation file for COMUSER.EXE. Has WinMain
               and CMainWindow implementation. Also has the main menu
               dispatching.
COMUSER.RC     The application resource definition file.
COMUSER.ICO    The application icon resource.
UTCRUCAR.H     The class declaration for the COUtilityCruiseCar COM object.
UTCRUCAR.CPP   Implementation file for the COUtilityCruiseCar COM object.
               Also has the definition of the CreateUtilityCruiseCar
               function.

This code sample is based on the code in DLLUSER, which in turn is based
on the code in EXESKEL. See the code tours in those samples for more
details on using the application skeleton and the DLL.

COMUSER uses many of the utility classes and services provided by APPUTIL.
For more details on APPUTIL, study the APPUTIL library source code and
APPUTIL.TXT, which are located in the sibling \APPUTIL directory.

COMUSER illustrates various kinds of COM objects in various settings. The
object created and used in COMUSER is of class COUtilityCruiseCar. It is
constructed by aggregation of the COCruiseCar COM object, which is
implemented in COMOBJ.DLL. See the COMOBJ code tour for more information
about the COCar, COUtilityCar, and COCruiseCar objects.

In addition to showing how COUtilityCruiseCar is constructed, this lesson
demonstrates how the COMUSER.EXE, as a client of the COM objects
implemented in both COMUSER and COMOBJ.DLL, creates, exercises, and
releases those objects.

We start in COMUSER.CPP. The following include statements are used to bring
in include files:

     #include <windows.h>
     #include <ole2.h>
     #include <initguid.h>
     #include <commdlg.h>
     #include <apputil.h>
     #include <icars.h>
     #include <carguids.h>
     #include <comobj.h>
     #include "comuser.h"
     #include "utcrucar.h"

Of special note is INITGUID.H. This file must be included only once in
each executable. It ensures that any GUIDs defined in this application
will be included as named data in the data segment of the EXE. If you
forget to include this file, you will get unresolved external references
to the IID_IMyInterface GUIDs during linking.

The ICARS.H and CARGUIDS.H files, located in the sibling \INC directory,
are included to get the car-related interface abstract base class
declarations and their associated GUID definitions.

The COMOBJ lesson introduced the MsgBox and MsgLog facilities, and their
use is continued here. The LOG* macro calls in the source code are trace
statements that log and display appropriate conditions at run time.

After the message log is created, a global MsgLog pointer is assigned.
Such global variables are usually avoided in object-oriented programming,
but they conveniently support the centrally defined logging macros used
throughout this code sample series. A definition of this same-named global
variable, g_pMsgLog, is also needed inside such subordinate DLLs as
COMOBJ.DLL for the logging macros to work there. Because of this, the
actual pMsgLog pointer for this current application's message log must be
passed to the DLL. The ComObjInitMsgLog call below passes this needed
pointer to the DLL so that its logged internal behavior is displayed in
COMUSER.EXE's logging display.

    // Create the Message Log ListBox as a child window in the Main Window.
    if (m_pMsgLog->Create(m_hInst, m_hWnd, TRUE))
    {
      // Assign the global MsgLog pointer.
      g_pMsgLog = m_pMsgLog;
      // Use macro to log messages.
      LOGID(IDS_START_MESSAGE_LOG);
      // Since we're exploiting the COMOBJ DLL and we want it to use
      // this same Message Log facility, we pass it a pointer to our
      // particular instance's CMsgLog object.
      ComObjInitMsgLog(g_pMsgLog);
    }

In the CMainWindow::DoMenu method, we see how the menu choices are
dispatched. We'll take a typical example from the COM object implemented
in this application. The following fragment shows the Create command from
the UtilityCruiseCar menu.

    case IDM_UCRU_CREATE:
      LOG("E: === UtilityCruiseCar Menu: Create.");
      if (NULL == m_pUtilityCruiseCar)
      {
        hr = CreateUtilityCruiseCar(
               NULL,
               IID_IUnknown,
               (PPVOID)&m_pUtilityCruiseCar);
        if (SUCCEEDED(hr))
        {
          ::CheckMenuItem(
              hMenu,
              IDM_CAR_CREATE,
              MF_BYCOMMAND | MF_CHECKED);
        }
        else
          LOG("C: ???? Car creation failed.");
      }
      else
        LOG("E: ???? UtilityCruiseCar already exists.");
      break;

The CreateUtilityCruiseCar function is called. This function is defined
in the UTCRUCAR.CPP file, part of this application. NULL is passed for
the first argument because at this "top" level of usage we are not
aggregating the new COUtilityCruiseCar object as part of some outer
object. Once the object is created, the output argument,
m_pUtilityCruiseCar, will write a pointer to the IUnknown interface of the
new object to m_pUtilityCruiseCar. We cache this object pointer for
subsequent use of the COM object. Following the rules of COM, the
reference count on this pointer is incremented with a call to AddRef from
the CreateUtilityCruiseCar function, and it will need to be decremented
with a call to Release when it is no longer needed. A checkmark is
displayed beside the UtilityCruiseCar/Create menu item to indicate that
there is currently an instance of this object.

The following fragment handles the matching release of the
COUtilityCruiseCar COM object.

    case IDM_UCRU_RELEASE:
      LOG("E: === UtilityCruiseCar Menu: Release.");
      if (NULL != m_pUtilityCruiseCar)
      {
        RELEASE_INTERFACE(m_pUtilityCruiseCar);
        ::CheckMenuItem(
            hMenu,
            IDM_CAR_CREATE,
            MF_BYCOMMAND | MF_UNCHECKED);
      }
      else
        LOG("E: ???? No UtilityCruiseCar to Release.");
      break;

The UtilityCruiseCar/Create menu item is unchecked to show that the
object no longer exists. The RELEASE_INTERFACE macro used above is defined
in APPUTIL.H:

      #define RELEASE_INTERFACE(p)\
      {\
        IUnknown* pTmp = (IUnknown*)p;\
        p = NULL;\
        if (NULL != pTmp)\
          pTmp->Release();\
      }

If the m_pUtilityCruiseCar object pointer is not NULL, a
COUtilityCruiseCar object currently exists, and RELEASE_INTERFACE calls
the interface's Release method. The pTmp pointer is used and the input
interface pointer p is set to NULL prior to release to provide more robust
behavior in multi-threaded programs.

A pointer like m_pUtilityCruiseCar is commonly referred to as a pointer to
the object. Technically, it is a pointer to the IUnknown interface of
that object. Once we have this pointer, we can access the object's other
interfaces and make the object do things by calling their methods. Here
is an example:

    case IDM_UCRU_WINCH:
      LOG("E: === UtilityCruiseCar Menu: IUtility::Winch");
      if (GetInterface(m_pUtilityCruiseCar, IID_IUtility, (PPVOID)&pIUtility))
      {
        LOG("E: --Calling pIUtility->Winch");
        pIUtility->Winch(30);
        LOG("E: --Releasing pIUtility");
        pIUtility->Release();
      }
      break;

This fragment calls the Winch method of the IUtility interface on the
COUtilityCruiseCar object. The GetInterface call, defined in COMUSER.CPP,
is a simple wrapper for IUnknown::QueryInterface. It is used to reduce
code clutter by isolating some of the logging activity associated with the
QueryInterface call in this tutorial. QueryInterface is called to obtain
the COUtilityCruiseCar object's IUtility interface. The pointer to
IUtility is used to call the Winch method. When the IUtility interface is
no longer needed, Release is called on the pIUtility pointer.

We now look at the UtilityCruiseCar COM object itself (in UTCRUCAR.H and
UTCRUCAR.CPP). The UtilityCruiseCar COM object class is declared in
UTCRUCAR.H.

  class COUtilityCruiseCar : public IUnknown
  {
    public:
      // Main Object Constructor & Destructor.
      COUtilityCruiseCar(IUnknown* pUnkOuter);
      ~COUtilityCruiseCar(void);

      // A general method for initializing a newly created UtilityCruiseCar.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

    private:
      // We declare nested class interface implementations here.

      // We implement the IUtility interface (ofcourse) in this
      // COUtilityCruiseCar COM object class. This is the native
      // interface that we are using as an augmentation to the
      // existing COCruiseCar COM object class.
      class CImpIUtility : public IUtility
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIUtility(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpIUtility(void);

          // IUnknown methods.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

          // IUtility methods.
          STDMETHODIMP Offroad(short nGear);
          STDMETHODIMP Winch(short nRpm);

        private:
          // Data private to this interface implementation of IUtility.
          ULONG               m_cRefI;     // Interface Ref Count (debug).
          COUtilityCruiseCar* m_pBackObj;  // Parent Object back pointer.
          IUnknown*           m_pUnkOuter; // Outer unknown for Delegation.
      };

      // Make the otherwise private and nested IUtility interface
      // implementation a friend to COM object instantiations of this
      // selfsame COUtilityCruiseCar COM object class.
      friend CImpIUtility;

      // Private data of COUtilityCruiseCar COM objects.

      // Nested IUtility implementation instantiation. This IUtility
      // interface is instantiated inside this COUtilityCruiseCar object
      // as a native interface.
      CImpIUtility     m_ImpIUtility;

      // Main Object reference count.
      ULONG            m_cRefs;

      // Outer unknown (aggregation delegation). Used when this
      // COUtilityCruiseCar object is itself being aggregated.
      IUnknown*        m_pUnkOuter;

      // We need to save the IUnknown interface pointer on the COCruiseCar
      // object that we aggregate. We use this when we need to delegate
      // IUnknown calls to this aggregated inner object.
      IUnknown*        m_pUnkCruiseCar;
  };

The IUtility interface is implemented in this COM object as a nested
class. The embedded CImpIUtility is declared in the private section of the
COUtilityCruiseCar object class. An instance of CImpIUtility is also
created:

     // Nested IUtility implementation instantiation. This IUtility
     // interface is instantiated inside this COUtilityCruiseCar object
     // as a native interface.
     CImpIUtility     m_ImpIUtility;

As we'll see, COUtilityCruiseCar is itself constructed by aggregating the
COCruiseCar object with this IUtility implementation. That is what the
m_pUnkCruiseCar pointer, also in the COUtilityCruiseCar private area, is
for. It caches a pointer to the aggregated COCruiseCar object. The
pointer is used to delegate certain IUnknown calls to the inner object's
IUnknown interface.

For the case when a COUtilityCruiseCar object is itself aggregated by an
outer COM object, it has an m_pUnkOuter delegation pointer in its private
data area. The value of this pointer is set when the COUtilityCruiseCar
object is created. Because COMUSER does not aggregate this object, it sets
the pointer to NULL.

CImpIUtility itself also has a private data member, m_pUnkOuter, for
supporting aggregation and delegation. This pointer is used to delegate
calls to the IUtility interface's IUnknown to either the
COUtilityCruiseCar object (when it is not aggregated) or to the
controlling IUnknown (when the COUtilityCruiseCar object is aggregated).
The m_pBackObj pointer is for convenient use inside any of the IUtility
method functions to reference members of the COUtilityCruiseCar object.

As an embedded class, CImpIUtility needs to be made a friend class to
COUtilityCruiseCar so that CImpIUtility's methods can access the private
data in COUtilityCruiseCar.

The method definitions for the COUtilityCruiseCar class are in
UTCRUCAR.CPP. As we observed in the COMOBJ code sample, the
COUtilityCruiseCar constructor uses a member initializer,
m_ImpIUtility(this, pUnkOuter), to pass the 'this' and pUnkOuter arguments
to the CImpIUtility constructor, which is executed just prior to the main
body of the COUtilityCruiseCar constructor. Both of the argument values
are available when the member initializer is executed. In these code
samples, the successful use of the nested classes technique for interface
implementations depends on the use of such member initializers. Here is
the constructor:

  COUtilityCruiseCar::COUtilityCruiseCar(IUnknown* pUnkOuter)
    : m_ImpIUtility(this, pUnkOuter)
  {
    // Zero the COM object's reference count.
    m_cRefs = 0;

    // No AddRef necessary if non-NULL, as this COM object's lifetime
    // is totally coupled with the controlling Outer object's lifetime.
    m_pUnkOuter = pUnkOuter;

    // Zero the pointer to the aggregated COCruiseCar object's IUnknown
    // interface (for delegation of IUnknown calls to it).
    m_pUnkCruiseCar = NULL;

    LOGF1("E: COUtilityCruiseCar Constructor. m_pUnkOuter=0x%X.", m_pUnkOuter);

    return;
  }

The COUtilityCruiseCar::Init method is almost identical to the
COCruiseCar::Init that we studied in the COMOBJ code sample.

The CreateCruiseCar function is implemented in COMOBJ.DLL. CreateCruiseCar
calls QueryInterface which assigns its output m_pUnkCruiseCar object
pointer. This is a pointer to the aggregated COCruiseCar object's
IUnknown. We cache this pointer in the COUtilityCruiseCar object for
future use in delegating IUnknown calls to COCruiseCar. An example of
such delegation is in COUtilityCruiseCar object's IUnknown definition of
QueryInterface.

  STDMETHODIMP COUtilityCruiseCar::QueryInterface(
                 REFIID riid,
                 PPVOID ppv)
  {
    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    if (IID_IUnknown == riid)
    {
      *ppv = this;
      LOG("E: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.");
    }
    else if (IID_IUtility == riid)
    {
      // This IUtility interface is implemented in this COUtilityCruiseCar
      // object and might be called a native interface of
      // COUtilityCruiseCar.
      *ppv = &m_ImpIUtility;
      LOG("E: COUtilityCruiseCar::QueryInterface. pIUtility returned.");
    }

    if (NULL != *ppv)
    {
      // We've handed out a pointer to an interface so obey the COM rules
      // and AddRef its reference count.
      ((LPUNKNOWN)*ppv)->AddRef();
      hr = NOERROR;
    }
    else if (IID_ICar == riid)
    {
      LOG("E: COUtilityCruiseCar::QueryInterface. ICar delegating.");
      // We didn't implement the ICar interface in this COUtilityCruiseCar
      // object. The aggregated inner object (CruiseCar) is contributing
      // the ICar interface to this present composite or aggregating
      // UtilityCruiseCar object. So, to satisfy a QI request for the
      // ICar interface, we delegate the QueryInterface to the inner
      // object's IUnknown.
      hr = m_pUnkCruiseCar->QueryInterface(riid, ppv);
    }
    else if (IID_ICruise == riid)
    {
      LOG("E: COUtilityCruiseCar::QueryInterface. ICruise delegating.");
      // We didn't implement the ICruise interface in this
      // COUtilityCruiseCar object. The aggregated inner object
      // (CruiseCar) is contributing the ICruise interface to this
      // present composite or aggregating UtilityCruiseCar object.
      // As above, we delegate this QI to the aggregated object's IUnknown.
      hr = m_pUnkCruiseCar->QueryInterface(riid, ppv);
    }

    return (hr);
  }

The IID_IUnknown and IID_IUtility cases are familiar, and if we pass
one of these interface pointers by request, we increment the interface's
reference count with a call to its AddRef method. The IID_IUtility
interface is exposed as a native interface on this COM object, so it's easy
to pass a pointer to it. For the IID_ICar and IID_ICruise interfaces,
however, we need to pass the request to the aggregated inner object where
they are implemented. We use the cached m_pUnkCruiseCar object pointer to
delegate the QueryInterface call to aggregated objects that are providing
implementations of those interfaces to the COUtilityCruiseCar COM object.

Prior to deleting COUtilityCruiseCar, the COUtilityCruiseCar::Release
method artificially increments the object's reference count, as was
discussed for the COCruiseCar object in the COMOBJ code sample.

The COUtilityCruiseCar::CImpIUtility::CImpIUtility constructor uses the
same logic to assign its own IUnknown delegation pointers as that
discussed for the COCruiseCar object in the COMOBJ code sample.

The COUtilityCruiseCar::CImpIUtility::Offroad method has been set up to
show something a little different. Here we show the aggregation rules
when an outer object needs to call methods of interfaces to the inner
aggregated objects. The Offroad method stops the car (sets its speed to
0) in order to shift the transfer case to offroad 4-wheel drive Low gear
(nGear = 3). In the COUtilityCruiseCar object's implementation of
IUtility::Offroad, we need to call the ICar interface's Speed method if
the Offroad request is for 4-wheel drive low speed. But this ICar
interface is implemented in the inner COCruiseCar object. We must call
QueryInterface to get a pointer to it and then carefully manage the
reference count on the outer object, for reasons we will discuss in a
moment.

  STDMETHODIMP COUtilityCruiseCar::CImpIUtility::Offroad(
                 short nGear)
  {
    HRESULT hr;
    ICar* pICar = NULL;

    // In our fantasy Sport-Utility Car world we may need to stop the car
    // before switching to 4-Wheel drive low (nGear == 3 for 4L). Let's
    // assume so because it's a convenient excuse to show this aggregating
    // COUtilityCruiseCar outer object using one of the interfaces (ICar)
    // of its aggregated COCruiseCar inner object. COMUSER gracefully
    // cooperates in this by invoking this Offroad method from its
    // UtilityCruiseCar menu with nGear == 3.
    if (3 == nGear)
    {
      hr = m_pBackObj->m_pUnkCruiseCar->QueryInterface(
                                          IID_ICar,
                                          (PPVOID)&pICar);
      if (SUCCEEDED(hr))
      {
        m_pUnkOuter->Release();
        pICar->Speed(0);
        m_pUnkOuter->AddRef();
        pICar->Release();
      }
    }

    LOGF1("E: COUtilityCruiseCar::CImpIUtility::Offroad. Called. nGear=%i.",nGear);

    return NOERROR;
  }

The interface's m_pBackObj pointer provides a way to access the cached
pointer to the aggregated COCruiseCar object, m_pUnkCruiseCar. Because
the QueryInterface method of the inner object is delegated as part of an
aggregated object, it will increment the reference count of the outermost
aggregating object by calling the AddRef method of the controlling
IUnknown. This outermost aggregating object may not be the
COUtilityCruiseCar object. Here's the problem. A aggregate object is
using an interface on itself from inside itself, but the call to AddRef is
delegated to the outermost aggregating object and thus falsely records an
outstanding external reference to it. As a result, the lifetime of that
outer aggregating object is no longer fully controlled by releases of
actual external references held to it.

To put the reference count in a better state for the duration that the
outer object holds a reference to an aggregated inner object's interface,
Release is explicitly called on the outer object after the QueryInterface
call (to offset the internal delegated AddRef call). The acquired inner
interface is now used and could be held for a lengthy period. To properly
release it, AddRef is called explicitly on the outer object, followed by a
call to Release on the acquired inner object's interface pointer. This
sequence is necessary to avoid recursive or reentrant destruction during
the process. This practice is recommended under Win32, especially if the
inner interface pointer is held for some time and if the inner object is
in a different process (or even on a different machine) from that of the
outer object.

At the end of UTCRUCAR.CPP is the CreateUtilityCruiseCar function, which
creates an instance of the COUtilityCruiseCar COM object. We saw a call to
this function above in the code for the UtilityCruiseCar menu's Create item.

  HRESULT CreateUtilityCruiseCar(
            IUnknown* pUnkOuter,
            REFIID riid,
            PPVOID ppv)
  {
    HRESULT hr;
    COUtilityCruiseCar* pCob;

    LOGF1("E: CreateUtilityCruiseCar. pUnkOuter=0x%X.",pUnkOuter);

    // If the creation call is requesting aggregation (pUnkOuter != NULL),
    // the COM rules state the IUnknown interface MUST also be concomitantly
    // requested. If it is not so requested (riid != IID_IUnknown), then
    // an error must be returned indicating that no aggregate creation of
    // the COUtilityCruiseCar COM Object can be performed using anything
    // other than a controlling IUnknown interface.
    if (NULL != pUnkOuter && riid != IID_IUnknown)
      hr = CLASS_E_NOAGGREGATION;
    else
    {
      // Instantiate a COUtilityCruiseCar COM Object.
      pCob = new COUtilityCruiseCar(pUnkOuter);
      if (NULL != pCob)
      {
        // If we have succeeded in instantiating the COUtilityCruiseCar
        // object, we initialize it to offer its interfaces.
        hr = pCob->Init();
        if (SUCCEEDED(hr))
        {
          // We QueryInterface this new COM Object not only to deposit the
          // main interface pointer to the caller's pointer variable, but to
          // also automatically bump the Reference Count on the new COM
          // Object after handing out this *ppv external reference to it.
          hr = pCob->QueryInterface(riid, (PPVOID)ppv);
        }
      }
      else
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
      LOGF1("E: CreateUtilityCruiseCar Succeeded. *ppv=0x%X.",*ppv);

    return hr;
  }

The arguments to this function permit the object to be created as part of
an even grander aggregation. If the first pUnkOuter parameter is non-NULL,
the COUtilityCruiseCar object is to be aggregated, and all the
subsequent logic will support this aggregation. For another such example,
see the code for the COCruiseCar object in the COMOBJ directory. An
instance of the COM object class is first created (memory is allocated for
it, and its constructor is executed). The object's Init() member function
is then called to create any subordinate objects and interfaces. In this
case, the COCruiseCar COM object is created and aggregated into the new
COUtilityCruiseCar object. If this step succeeds, QueryInterface is
called to obtain the IUnknown of the outer object. On the way, this
writes that object pointer at the location specified by the ppv output
argument. On the way, this also calls the AddRef method to increment the
reference count on the new object, because a pointer to it was passed to
an outside consumer--in this case, COMUSER.EXE.

You can experiment with the COMUSER menus to watch the internal activity
of these COM objects. Here we will tour some representative trace logs.
This one is for the menu selection UtilityCruiseCar\Create:

  E: === UtilityCruiseCar Menu: Create.
  E: CreateUtilityCruiseCar. pUnkOuter=0x0.
  E: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  E: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  E: COUtilityCruiseCar::Init.
  D: CreateCruiseCar. pUnkOuter=0x770694.
  D: COCruiseCar::CImpICruise Constructor. Aggregating.
  D: COCruiseCar Constructor. m_pUnkOuter=0x770694.
  D: COCruiseCar::Init.
  D: CreateCar. pUnkOuter=0x770694.
  D: COCar::CImpICar Constructor. Aggregating.
  D: COCar Constructor. m_pUnkOuter=0x770694.
  D: COCar::QueryInterface. 'this' pIUnknown returned.
  D: COCar::AddRef. New cRefs=1.
  D: CreateCar Succeeded. *ppv=0x660698.
  D: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  D: COCruiseCar::AddRef. New cRefs=1.
  D: CreateCruiseCar Succeeded. *ppv=0x660674.
  E: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  E: COUtilityCruiseCar::AddRef. New cRefs=1.
  E: CreateUtilityCruiseCar Succeeded. *ppv=0x770694.

The "E:" or "D:" at the beginning of each line indicates, respectively, that
execution was in the .EXE or in the .DLL.

First, COMUSER calls CreateUtilityCruiseCar (passing a NULL pUnkOuter,
because it is simply creating a COUtilityCruiseCar object without
aggregating one). Then a COUtilityCruiseCar object is created, causing
first the constructor for the IUtility implementation and then that for
the COUtilityCruiseCar object to be executed. COUtilityCruiseCar::Init is
then called, and it calls the CreateCruiseCar function in COMOBJ.DLL to
create an aggregated COCruiseCar object. This object is to be aggregated,
so a non-NULL pUnkOuter is passed. When the COCruiseCar COM object is
created, the constructors for CImpICruise and then COCruiseCar are called.
The CImpICruise interface constructor is passed the non-NULL pUnkOuter.
This ICruise interface implementation will later use this pointer to
delegate its IUnknown calls to the controlling IUnknown. Next,
CreateCruiseCar calls the COCruiseCar object's Init method to create any
subordinate COM objects.

Because a COCruiseCar object obtains its ICar features from an aggregated
COCar COM object, CreateCar is called within COMOBJ.DLL. As before, we
see the constructors for CImpICar and COCar and that they are passed a
non-NULL pUnkOuter, indicating aggregation. The same pUnkOuter value is
passed to these nested aggregated objects. This is how all of them can
delegate directly to the controlling IUnknown. As we back out of the call
tree, each create function uses a call to QueryInterface to pass a pointer
to an appropriate IUnknown and to increment (via calls to AddRef) the
appropriate reference count. Each object then ends up with a reference
count of 1.

Now that we have created a UtilityCruiseCar, here's the trace log for menu
selection UtilityCruiseCar\ICar::Shift:

  E: === UtilityCruiseCar Menu: ICar::Shift
  E: --Obtaining Interface Pointer.
  E: COUtilityCruiseCar::QueryInterface. ICar delegating.
  D: COCruiseCar::QueryInterface. ICar delegating.
  D: COCar::QueryInterface. pICar returned.
  D: COCar::CImpICar::Addref. Delegating. New cI=1.
  E: COUtilityCruiseCar::AddRef. New cRefs=2.
  E: Interface obtained. *ppv=0x6606A4
  E: --Calling pICar->Shift
  D: COCar::CImpICar::Shift. Called. nGear=1.
  E: --Releasing pICar
  D: COCar::CImpICar::Release. Delegating. New cI=0.
  E: COUtilityCruiseCar::Release. New cRefs=1.

First, an ICar interface pointer on the COUtilityCruiseCar object must be
obtained by calling QueryInterface. This call delegates to the next inner
object that has the interface being asked for--in this case, the
COCruiseCar object. This COM object's QueryInterface in turn does the same
thing: it delegates the request to the ICar interface that is implemented
in the COCar COM object. The pICar pointer is finally obtained from the
COCar object's QueryInterface. The AddRef call from QueryInterface is
delegated directly to the controlling IUnknown (in this case,
COUtilityCruiseCar::AddRef). This call increments the COUtilityCruiseCar
object's reference count to 2. An outside agent is now holding a
reference to the ICar interface on this aggregate object. That same
outside agent (COMUSER.EXE) has also been holding a reference to the
object's IUnknown, which explains the reference count of 2. The ICar
interface pointer is used to directly call the implementation of the Shift
method in the COCar object. The pointer to the ICar interface is then
released, which causes a delegation to the controlling IUnknown's Release
method, upon which the object's reference count is decremented to a 1.

Here's the trace log for menu item UtilityCruiseCar\Release:

  E: === UtilityCruiseCar Menu: Release.
  E: COUtilityCruiseCar::Release. New cRefs=0.
  E: COUtilityCruiseCar::Destructor.
  D: COCruiseCar::Release. New cRefs=0.
  D: COCruiseCar::Destructor.
  D: COCar::Release. New cRefs=0.
  D: COCar::Destructor.
  D: COCar::CImpICar Destructor.
  D: COCruiseCar::CImpICruise Destructor.
  E: COUtilityCruiseCar::CImpIUtility Destructor.

This menu item causes a cascade of releases and object destruction. The
initial call to Release decrements the outstanding reference count of
1 to 0, causing the entire COUtilityCruiseCar COM object to be deleted.
The object's destructor is called, which has a call to Release the
aggregated COCruiseCar COM object. That object's reference count is
decremented from 1 to 0, causing the COCruiseCar object to be deleted.
As above, its destructor releases the aggregated COCar object, decrementing
its reference count to 0 and causing its destructor to run. As we wind
back out of all these destructors, we see the automatic destructors of the
ICar, ICruise,and IUtility "ImpI" interface implementations. These required
no explicit deletions, because the interfaces are implemented as nested
classes. This is one of the benefits of the nested classes technique.
