LOCCLIEN - Client of Out-of-process Local Server


SUMMARY
=======

The LOCCLIEN sample presents the same series of car-related components
that were studied in previous lessons. The LOCCLIEN/LOCSERVE client/server
pair is analogous to the DLLCLIEN/DLLSERVE pair. The main difference is
that LOCCLIEN must cross process boundaries to access components in the
out-of-process local server LOCSERVE. In contrast, DLLCLIEN could directly
access components in the in-process server DLLSERVE. The LOCCLIEN lesson
illustrates how a client can access and control components in an
out-of-process local server.

The COM objects that LOCCLIEN manipulates are the car-related ones of
previous samples, with the following interfaces: ICar, IUtility, and
ICruise. LOCCLIEN works in conjunction with the separate LOCSERVE.EXE,
which provides the COCar, COUtilityCar, and COCruiseCar COM objects.

LOCCLIEN.EXE creates its own COUtilityCruiseCar COM object, which is
constructed by reusing the COCruiseCar COM object by containment and
augmenting it with a native implementation of the IUtility interface.
Because the COCruiseCar COM object class is an aggregate--that is, it
reuses an inner COCar object by aggregation--LOCCLIEN illustrates nesting
COM objects by mixed reuse. The composite COUtilityCruiseCar object is
constructed by containment reuse of COCruiseCar, another composite COM
object. COCruiseCar is constructed by aggregation reuse of the COCar COM
object. The composition of COUtilityCruiseCar is also interesting because
the COUtilityCruiseCar object's containment of the COCruiseCar object
crosses the process boundary between LOCCLIEN and the out-of-process local
server LOCSERVE.EXE. LOCCLIEN thus relies on standard marshaling support
for the custom interfaces it uses on the COCruiseCar object. This
marshaling support was separately built as MARSHAL.DLL in the earlier
MARSHAL code sample.

For functional descriptions and a tutorial code tour of LOCCLIEN, see the
Code Tour section below. See also LOCSERVE.TXT in the sibling \LOCSERVE
directory for more details on how LOCSERVE works and exposes its services
to LOCCLIEN. You must build LOCSERVE.EXE before building LOCCLIEN. The
makefile for LOCSERVE automatically registers that server in the registry,
so you must build LOCSERVE before attempting to run LOCCLIEN. For details
on the external user operation of LOCCLIEN, see the Operation section
below.

Because client and server reside in separate processes, both LOCCLIEN and
LOCSERVE rely on marshaling for the ICar, IUtility, and ICruise interfaces.
This support was provided by the previous MARSHAL code sample, so you must
also build the MARSHAL code sample prior to building and running LOCCLIEN
and LOCSERVE.

For details on setting up your system to build and test the code samples
in this OLE Tutorial series, see TUTORIAL.TXT. The supplied MAKEFILE is
Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE
command in the Command Prompt window.

Usage
-----

LOCCLIEN is an application that you can execute directly from Windows in
the normal manner or from the Command Prompt window. No command line
parameters are recognized by LOCCLIEN.


OPERATION
=========

The LOCCLIEN.EXE application provides the user interface for this lesson.
It exercises the associated, but independent, LOCSERVE.EXE out-of-process
local server. Here is a summary of operation from the standpoint of
LOCCLIEN.EXE as a COM client of the LOCSERVE.EXE COM server.

The LOCCLIEN and LOCSERVE samples are directly analagous to the DLLCLIEN
and DLLSERVE samples. The same components are used, and the same menu
system exercises those objects. The main difference is that the client
must use the standard marshaling support provided by the MARSHAL.DLL
handler for the interfaces that reside across process boundaries. This
standard marshaling is largely transparent if you use the Microsoft
Interface Definition Language (MIDL) compiler and build a standard
marshaling DLL.

As in the LOCSERVE code sample, support is provided for a trace logging
facility that will display in the client an integrated trace log of
activity in both client and server. Logging activity in this lesson is
complicated by the process boundary and the methods of achieving such
logging are covered in this lesson.

Although LOCCLIEN functions much like the prior DLLCLIEN and COMUSER code
samples, we'll give a short review for those readers who are visiting
this code sample out of sequence. The COM objects that are used in the
LOCCLIEN and LOCSERVE 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.

LOCCLIEN.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 LOCSERVE.EXE. For details, see the
LOCSERVE lesson. COCruiseCar is constructed using aggregation and is
also implemented in LOCSERVE.EXE. COUtilityCruiseCar is constructed using
containment and is implemented in LOCCLIEN.EXE. In this sample,
COUtilityCruiseCar reuses COCruiseCar by containment to illustrate nested
object reuse with a mix of aggregation and containment.

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

Menu Selection: File/Exit
Exits LOCCLIEN.

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 LOCCLIEN.TXT
Opens the LOCCLIEN.TXT file (this file) in the Windows Notepad.

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

Menu Selection: Help/Read MARSHAL.TXT
Opens the MARSHAL.TXT file from the sibling \MARSHAL 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 LOCCLIEN
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 LOCSERVE
Displays the About dialog box for LOCSERVE.EXE, which is used by this
application. In this series of code samples, partner servers like LOCSERVE
are given their own About dialog box in the native resources of the server.
This menu item calls the server function that displays this dialog box.


CODE TOUR
=========

Files          Description

LOCCLIEN.TXT   This file.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
LOCCLIEN.H     The include file for the LOCCLIEN application. Contains
               class declarations, function prototypes, and resource
               identifiers.
LOCCLIEN.CPP   The main implementation file for LOCCLIEN.EXE. Has WinMain
               and CMainWindow implementation, as well as the main menu
               dispatching.
LOCCLIEN.RC    The application resource definition file.
LOCCLIEN.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.

Like all code samples in the series, LOCCLIEN 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.

LOCCLIEN is consistent with earlier lessons to highlight the comparison
between out-of-process local servers with in-process servers. LOCCLIEN
retains the same set of COM objects as the previous DLLCLIEN lesson. A
matching menu system to manipulate these objects is also retained. For
details on the implementation and functionality of these COM objects, see
the code tours for DLLCLIEN and DLLSERVE.

The goal of this lesson is to examine the client code needed to support
cross-process manipulation of the components in the LOCSERVE server. We
will tour internal behavior and examine a representative trace log.

The client remains largely the same when we move the car-related COM
objects from an in-process server housing (DLLSERVE) to an out-of-process
server housing (LOCSERVE). The most important change, while also
convenient for instructional variety, is actually dictated by the nature
of the current COM marshaling support for aggregation. The native
COUtilityCruiseCar COM object in the DLLCLIEN lesson illustrated three
levels of nested aggregation. COUtilityCruiseCar aggregated a native
IUtility interface implementation with the COCruiseCar object housed in
DLLSERVE. The COCruiseCar object, in turn, aggregated an ICruise interface
implementation with the COCar object housed in DLLSERVE. This nested
aggregation worked well, even though the aggregation spanned an object in
the client and an inner object in the server. Despite the client/server
distinction, however, the server was in the same process as the client.
(DLLSERVE was an in-process server.)

With LOCCLIEN, however, the aggregation that spans the client and server
would have to span a process boundary. The standard marshaling currently
provided for the CreateInstance method of IClassFactory does not accept a
non-NULL pointer to the controlling IUnknown (pUnkOuter). The
CLASS_E_NOAGGREGATION error is returned by the marshaling proxy created in
the previous MARSHAL code sample. What this means is that while
aggregation is a powerful, efficient, and recommended way to reuse COM
objects, it is currently supported only for in-process COM objects. For
composite COM objects that must span process, apartment model thread, or
machine boundaries, COM objects must be reused by containment.

In containment reuse, the composite object implements the interfaces of
the contained inner object, creates an instance of the contained object,
and caches pointers to the contained object's interfaces. The outer
object implements delegating interfaces for the contained object's
interfaces. The methods in these interfaces delegate their calls to the
corresponding methods in the interfaces of the contained object. This
delegation is done using the cached pointers to those interfaces.
Delegation was covered in earlier lessons, where the COUtilityCar COM
object was studied. The technique is employed in a similar way in
LOCCLIEN to construct the COUtilityCruiseCar COM object. We'll now
examine this in detail.

Here is the class declaration for COUtilityCruiseCar from 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 members. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // We get this ICar interface pointer via containment reuse of the
      // ICar interface in an instantiated COCruiseCar.
      ICar*                m_pICar;

      // We get this ICruise interface pointer via containment reuse of the
      // ICruise interface in an instantiated COCruisecar.
      ICruise*             m_pICruise;

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

      // We implement the ICar interface in this COUtilityCruiseCar COM
      // object class. This is a delegating interface for the one
      // in the contained COCruiseCar COM object.
      class CImpICar : public ICar
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICar(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICar(void);

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

          // ICar methods.
          STDMETHODIMP         Shift(short nGear);
          STDMETHODIMP         Clutch(short nEngaged);
          STDMETHODIMP         Speed(short nMph);
          STDMETHODIMP         Steer(short nAngle);

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

      // We implement the ICruise interface in this COUtilityCruiseCar COM
      // object class. This ICruise implementation is a delegating
      // interface for the one provided in the contained COCruiseCar COM
      // object.
      class CImpICruise : public ICruise
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpICruise(COUtilityCruiseCar* pBackObj, IUnknown* pUnkOuter);
          ~CImpICruise(void);

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

          // ICruise methods.
          STDMETHODIMP Engage(BOOL bOnOff);
          STDMETHODIMP Adjust(BOOL bUpDown);

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

      // We implement the IUtility interface 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 members.
          STDMETHODIMP         QueryInterface(REFIID, PPVOID);
          STDMETHODIMP_(ULONG) AddRef(void);
          STDMETHODIMP_(ULONG) Release(void);

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

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

      // Make the otherwise private and nested IUtility, ICruise, and ICar
      // interface implementations friend instantiations of this class.
      friend CImpICar;
      friend CImpICruise;
      friend CImpIUtility;

      // Private data of COUtilityCruiseCar COM objects.

      // Nested ICar implementation instantiation.
      CImpICar        m_ImpICar;

      // Nested ICruise implementation instantiation.
      CImpICruise     m_ImpICruise;

      // Nested IUtility implementation instantiation. This IUtility
      // interface is implemented 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 being aggregated.
      IUnknown*       m_pUnkOuter;
  };

As in all these code samples, the multiple interfaces are implemented as
nested classes. Here are nested implementations of ICar, ICruise, and
IUtility. If we were aggregating the COCruiseCar COM object rather than
containing it, we would not have to nest the ICruise and ICar interface
implementations; we would need only the cached pointers to those
interfaces (m_pICar and m_pICruise above). With aggregation, such
pointers provide direct access to the interfaces. With containment,
however, the containing COM object must implement delegating interfaces
for the interfaces used on the contained object. In this code example,
the COCruiseCar object is contained, and its ICar and ICruise interfaces
are implemented in LOCCLIEN as delegating interfaces.

Thus, we have implementations for all three interfaces. But since
IUtility is a native implementation in this COUtilityCruiseCar COM object
in the client, we only cache pointers to the interfaces exposed by the
contained COCruiseCar object (m_pICar and m_pICruise).

The COUtilityCruiseCar::Init method from UTCRUCAR.CPP assigns those
pointers.

  HRESULT COUtilityCruiseCar::Init(void)
  {
    HRESULT hr;
    IClassFactory* pICFCruiseCar;

    // Set up the right pIUnknown for delegation. If we are being
    // aggregated then we pass the pUnkOuter in turn to any COM objects
    // that we are aggregating. m_pUnkOuter was set in the Constructor.
    IUnknown* pUnkOuter = (NULL == m_pUnkOuter) ? this : m_pUnkOuter;

    LOGF1("C: COUtilityCruiseCar::Init. pUnkOuter=0x%X",pUnkOuter);

    // Get a class factory for LocCruiseCar and issue IClassFactory's
    // CreateInstance method to manufacture a COCruiseCar COM object.
    LOG("C: COUtilityCruiseCar::Init. Obtain CruiseCar Class Factory.");
    hr = CoGetClassObject(
           CLSID_LocCruiseCar,
           CLSCTX_LOCAL_SERVER,
           NULL,
           IID_IClassFactory,
           (PPVOID)&pICFCruiseCar);
    if (SUCCEEDED(hr))
    {
      LOG("C: COUtilityCruiseCar::Init. CruiseCar Class Factory obtained.");
      LOG("C: COUtilityCruiseCar::Init. Calling CFCruiseCar::CreateInstance.");
      // A NULL pUnkOuter is passed indicating that we are creating a
      // COCruiseCar component via the Containment reuse method. We can
      // currently pass nothing other than this NULL pUnkOuter because
      // aggregation is not supported across process or machine boundaries.
      // Since we are not requesting a COCruiseCar create for aggregation we
      // can directly request for the ICruise interface on the new object.
      hr = pICFCruiseCar->CreateInstance(
                            NULL,
                            IID_ICruise,
                            (PPVOID)&m_pICruise);
      LOG("C: COUtilityCruiseCar::Init. Releasing CruiseCar Class Factory.");
      pICFCruiseCar->Release();
      if (SUCCEEDED(hr))
      {
        // Now that we have a valid interface pointer (m_pICruise) on the
        // new object, we can obtain and cache a pointer to that contained
        // object's ICar interface as well. We must QI using the obtained
        // ICruise pointer on the object to ensure that the marshaling proxy
        // is involved in the QueryInterface call to provide the pointer to
        // the ICar interface proxy.
        hr = m_pICruise->QueryInterface(IID_ICar, (PPVOID)&m_pICar);
      }
    }

    if (SUCCEEDED(hr))
    {
      LOG("C: COUtilityCruiseCar::Init (CruiseCar Containment) Succeeded.");
    }
    else
    {
      LOG("C: COUtilityCruiseCar::Init (CruiseCar Containment) Failed.");
    }

    return (hr);
  }

Instead of the separate calls to CoGetClassObject and CreateInstance, we
could have made a single call to CoCreateInstance. The two-step sequence
is shown here for instructional purposes. The CoGetClassObject call
specifies that the server execution context is on the local machine
(CLSCTX_LOCAL_SERVER), and the CreateInstance call specifies NULL for the
pUnkOuter argument. Because execution context parameter is
CLSCTX_LOCAL_SERVER, a non-NULL pUnkOuter would return a
CLASS_E_NOAGGREGATION error without ever actually calling the
IClassFactory::CreateInstance implementation in the server. The NULL
pUnkOuter means we are creating a LocCruiseCar component, which is reused
by containment.

The successful CreateInstance call will store the m_pICruise pointer for
us. This pointer will actually point into the client's own process
space--to the ICruise interface proxy provided by the MARSHAL.DLL
marshaling server we built previously. COM will have loaded this DLL on
our behalf automatically as part of its support for standard marshaling.
COM loads MARSHAL.DLL because the CreateInstance call specified an
interface (IID_ICar) that is registered as a marshaled interface. As seen
in the MARSHAL lesson, the registry entry for the IID_ICar interface has
the path to its associated marshaling handler. From the client's
perspective, then, the marshaling is transparent, and the contained object
appears as if it were in the client process.

Once we get the pointer to the ICruise interface (m_pICar), we use it to
call QueryInterface on the ICar interface of the contained COCruiseCar
object. As in the server, we include MICARS.H for the interfaces that are
marshaled. MICARS.H is generated during the build of the MARSHAL code
sample.

How do those cached interface pointers complete support for containment?
Here's an example from UTCRUCAR.CPP.

  STDMETHODIMP COUtilityCruiseCar::CImpICruise::Engage(
                 BOOL bOnOff)
  {
    LOG("C: COUtilityCruiseCar::CImpICruise::Engage Delegating.");

    // Delegate this call to the contained COCruiseCar's ICruise::Engage.
    m_pBackObj->m_pICruise->Engage(bOnOff);

    return NOERROR;
  }

This is the delegating implementation of the ICruise::Engage method. The
interface implementation is nested, so the m_pBackObj pointer provides
access to the cached m_pICruise pointer. It is then used to make the
delegated call to the real interface in the contained COCruiseCar object.
When this call is made, it goes through the standard marshaling proxy for
this method of the ICruise interface. We built this standard marshaling
support in the MARSHAL code sample.

LOCCLIEN supports a Help/About LOCSERVE menu selection. Because the server
is in a different process, we can't directly address the server's address
space to display its About dialog box. Here's how it's done (from
CMainWindow::DoMenu in LOCCLIEN.CPP).

    case IDM_HELP_ABOUTSERVER:
      {
        // Post a message to the Server to show its AboutBox dialog box.
        LOG("C: === Help Menu: About LOCSERVE.");

        HWND hWnd = FindWindow(NULL, TEXT(SERVER_WINDOW_TITLE_STR));
        if (NULL != hWnd)
          PostMessage(hWnd, WM_COMMAND, IDM_HELP_ABOUT, NULL);
      }
      break;

The FindWindow function is called to find the window handle of the
running local server. It can get the window handle even if the server is
running hidden. The appropriate WM_COMMAND message is posted to the
server application, which honors it as though a user had selected it from
the server's menu. For this logic to work, both server and client must
have the same defintion for IDM_HELP_ABOUT. This menu item works only if
LOCCLIEN has already created a COM object provided by the LOCSERVE server.
Until then, the server is not loaded and so cannot receive the posted
message and display its About dialog box.

For symmetry, we'll list and tour a trace log of the same sequence of menu
actions that we studied in the DLLCLIEN and COMUSER lessons. Here is the
trace log for the Create command from the UtilityCruiseCar menu. Trace
lines that begin with "C:" are from the LOCCLIEN.EXE client. Lines that
begin with "L:" are from the LOCSERVE.DLL server.

  C: === UtilityCruiseCar Menu: Create.
  C: CreateUtilityCruiseCar. pUnkOuter=0x0.
  C: COUtilityCruiseCar::CImpICar Constructor. Non-Aggregating.
  C: COUtilityCruiseCar::CImpICruise Constructor. Non-Aggregating.
  C: COUtilityCruiseCar::CImpIUtility Constructor. Non-Aggregating.
  C: COUtilityCruiseCar Constructor. m_pUnkOuter=0x0.
  C: COUtilityCruiseCar::Init. pUnkOuter=0x870E2C.
  C: COUtilityCruiseCar::Init. Obtain CruiseCar Class Factory.

After the command is selected in the client, the CreateUtilityCruiseCar
function is called. It creates a new COUtilityCruiseCar object. The
interface constructors and the main object constructor are executed. The
Init method is called to create any inner objects. In this case there is
one, COCruiseCar. Init calls the COM function CoGetClassObject to obtain
the CruiseCar class factory.

  L: LOCSERVE now logging to client.
  L: CServer::OpenFactories. Begin.
  L: CFCar::CImpIClassFactory Constructor. Non-Aggregating.
  L: CFCar Constructor. m_pUnkOuter=0x0.
  L: CFCar::AddRef. New cRefs=1.
  L: CFCar::AddRef. New cRefs=2.
  L: CFUtilityCar::CImpIClassFactory Constructor. Non-Aggregating.
  L: CFUtilityCar Constructor. m_pUnkOuter=0x0.
  L: CFUtilityCar::AddRef. New cRefs=1.
  L: CFUtilityCar::AddRef. New cRefs=2.
  L: CFCruiseCar::CImpIClassFactory Constructor. Non-Aggregating.
  L: CFCruiseCar Constructor. m_pUnkOuter=0x0.
  L: CFCruiseCar::AddRef. New cRefs=1.
  L: CFCruiseCar::AddRef. New cRefs=2.
  L: CServer::OpenFactories. End.

COM runs the LOCSERVE.EXE server, because the CLSID passed with the class
factory request is in the system registry. The server creates all of its
supported class factories at once by calling the CServer::OpenFactories
method. AddRef is called twice to increment the class factory's reference
count, once by the server itself and once by COM when the OpenFactories
function registers the class factory with COM.

  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L: CFCruiseCar::AddRef. New cRefs=3.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L: CFCruiseCar::AddRef. New cRefs=4.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCruiseCar::AddRef. New cRefs=5.
  L: CFCruiseCar::AddRef. New cRefs=6.
  L: CFCruiseCar::Release. New cRefs=5.
  L: CFCruiseCar::AddRef. New cRefs=6.
  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCruiseCar::AddRef. New cRefs=7.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCruiseCar::Release. New cRefs=6.
  L: CFCruiseCar::QueryInterface. pIClassFactory returned.
  L: CFCruiseCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCruiseCar::AddRef. New cRefs=7.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCruiseCar::Release. New cRefs=6.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L: CFCruiseCar::Release. New cRefs=5.
  C: COUtilityCruiseCar::Init. CruiseCar Class Factory obtained.

This sequence begins with a client request for a specific class factory
(CoGetClassObject). The server is then loaded, and all the server's class
factories are subsequently created and registered. The log above is a
record of COM and the default marshaling for IClassFactory making various
QueryInterface and AddRef calls to the CFCruiseCar class factory. The
class factory ends up with five outstanding references (cRefs = 5) when
control returns successfully to the client and a class factory is
obtained.

  C: COUtilityCruiseCar::Init. Calling CFCruiseCar::CreateInstance.
  L: CFCruiseCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x0.
  L: COCruiseCar::CImpICruise Constructor. Non-Aggregating.
  L: COCruiseCar Constructor. m_pUnkOuter=0x0.
  L: CServer::ObjectsUp. New cObjects=1.
  L: COCruiseCar::Init.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L: CFCar::AddRef. New cRefs=3.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=2.
  L: CFCar::AddRef. New cRefs=4.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::CImpIClassFactory::QueryInterface. Delegating.
  L: CFCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCar::AddRef. New cRefs=5.
  L: CFCar::AddRef. New cRefs=6.
  L: CFCar::Release. New cRefs=5.
  L: CFCar::AddRef. New cRefs=6.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCar::AddRef. New cRefs=7.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCar::Release. New cRefs=6.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=3.
  L: CFCar::AddRef. New cRefs=7.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=2.
  L: CFCar::Release. New cRefs=6.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=1.
  L: CFCar::Release. New cRefs=5.
  L: CFCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCar::AddRef. New cRefs=6.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L: CFCar::Release. New cRefs=5.
  L: CFCar::Release. New cRefs=4.
  L: CFCar::Release. New cRefs=3.
  L: CFCar::QueryInterface. pIClassFactory returned.
  L: CFCar::CImpIClassFactory::Addref. Delegating. New cI=1.
  L: CFCar::AddRef. New cRefs=4.
  L: CFCar::Release. New cRefs=3.
  L: CFCar::CImpIClassFactory::CreateInstance. pUnkOuter=0x870F00.
  L: COCar::CImpICar Constructor. Aggregating.
  L: COCar Constructor. m_pUnkOuter=0x870F00.
  L: CServer::ObjectsUp. New cObjects=2.
  L: COCar::QueryInterface. 'this' pIUnknown returned.
  L: COCar::AddRef. New cRefs=1.
  L: CFCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870F28.
  L: CFCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L: CFCar::Release. New cRefs=2.
  L: COCruiseCar::Init (New Aggregation of COCar) Succeeded.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=1.
  L: COCruiseCar::AddRef. New cRefs=1.
  L: CFCruiseCar::CImpIClassFactory::CreateInstance Succeeded. *ppv=0x870F04.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=2.
  L: COCruiseCar::AddRef. New cRefs=2.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::CImpICruise::QueryInterface. Delegating.
  L: COCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L: COCruiseCar::AddRef. New cRefs=3.
  L: COCruiseCar::AddRef. New cRefs=4.
  L: COCruiseCar::Release. New cRefs=3.
  L: COCruiseCar::AddRef. New cRefs=4.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3.
  L: COCruiseCar::AddRef. New cRefs=5.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=2.
  L: COCruiseCar::Release. New cRefs=4.
  L: COCruiseCar::QueryInterface. pICruise returned.
  L: COCruiseCar::CImpICruise::Addref. Delegating. New cI=3.
  L: COCruiseCar::AddRef. New cRefs=5.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=2.
  L: COCruiseCar::Release. New cRefs=4.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=1.
  L: COCruiseCar::Release. New cRefs=3.

Most of the internal activity logged above is a history of COM and the
server creating the COCruiseCar object by aggregating a COCar object, also
in the server. The CFCar class factory was invoked to create and
aggregate a COCar object for the new COCruiseCar. We also see the events
that return the ICruise interface pointer originally requested in the call
to CreateInstance. When this activity is completed, the object count for
the LOCSERVE server (cObjects) is 2: one for the inner COCar object and
one for the COCruiseCar object itself.

  C: COUtilityCruiseCar::Init. Releasing CruiseCar Class Factory.
  L: CFCruiseCar::CImpIClassFactory::Release. Delegating. New cI=0.
  L: CFCruiseCar::Release. New cRefs=4.
  L: CFCruiseCar::Release. New cRefs=3.
  L: CFCruiseCar::Release. New cRefs=2.
  L: COCruiseCar::QueryInterface. ICar delegating.
  L: COCar::QueryInterface. pICar returned.
  L: COCar::CImpICar::Addref. Delegating. New cI=1.
  L: COCruiseCar::AddRef. New cRefs=4.
  L: COCar::CImpICar::Release. Delegating. New cI=0.
  L: COCruiseCar::Release. New cRefs=3.
  L: COCruiseCar::QueryInterface. ICar delegating.
  L: COCar::QueryInterface. pICar returned.
  L: COCar::CImpICar::Addref. Delegating. New cI=1.
  L: COCruiseCar::AddRef. New cRefs=4.
  C: COUtilityCruiseCar::Init (CruiseCar Containment) Succeeded.
  C: COUtilityCruiseCar::QueryInterface. 'this' pIUnknown returned.
  C: COUtilityCruiseCar::AddRef. New cRefs=1.
  C: CreateUtilityCruiseCar Succeeded. *ppv=0x870E2C.

When the client (still inside COUtilityCruiseCar::Init) has successfully
created the inner COCruiseCar object, it releases the class factory
pointer it had obtained. This results in a cascade of other releases,
some of which are done from inside COM. Init then calls QueryInterface
for the ICar interface to cache its address. Control eventually returns
to the client, where a COUtilityCruiseCar is successfully created. The
client holds a pointer to the IUnknown of COUtilityCruiseCar, and we see
the QueryInterface and AddRef calls that obtain this reference. The
reference count on this client-side object (cRefs) is only 1. All of the
preceding activity was due to one menu selection: UtilityCruiseCar/Create.

Next, we exercise one of the interfaces on this newly created composite
object. This interface is actually provided by an inner object (COCar)
that is aggregated in a COCruiseCar object, which in turn is contained in
the COUtilityCruiseCar object. Here is the ICar::Shift command from the
UtilityCruiseCar menu.

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

This is not too complicated because everything, including the marshaling
proxies, is set up. The client's QueryInterface method obtains a pointer
to ICar (pICar) and calls ICar::Shift. Because the composite object gets
this interface from a contained inner object, the call to Shift makes its
way through the delegation interface and is executed directly in the
server. Once Shift is called, the client releases the pICar pointer. This
call to Release is also delegated--in this case, to the controlling
IUnknown's Release--leaving a final reference count on COUtilityCruiseCar
of 1 (reflecting the IUnknown pointer the client has on this created
object). The ICar interface is released; the COUtilityCruiseCar object
exists until we choose the Release command from the UtilityCruiseCar menu.
Here it is:

  C: === UtilityCruiseCar Menu: Release.
  C: COUtilityCruiseCar::Release. New cRefs=0.
  C: COUtilityCruiseCar::Destructor.
  L: COCruiseCar::CImpICruise::Release. Delegating. New cI=0.
  L: COCruiseCar::Release. New cRefs=3.
  L: COCar::CImpICar::Release. Delegating. New cI=0.
  L: COCruiseCar::Release. New cRefs=2.
  L: COCruiseCar::Release. New cRefs=1.
  L: COCruiseCar::Release. New cRefs=0.
  L: CServer::ObjectsDown. New cObjects=1.
  L: COCruiseCar::Destructor.
  L: COCar::Release. New cRefs=0.
  L: CServer::ObjectsDown. New cObjects=0.
  L: CServer::ObjectsDown. Closing down LOCSERVE server.
  L: COCar::Destructor.
  L: COCar::CImpICar Destructor.
  L: COCruiseCar::CImpICruise Destructor.

In this scenario, COUtilityCruiseCar is the only object we created from
the client. This is contrived so we can show how the out-of-process local
server is unloaded. In the log entries above, we see the cascade of
releases and destructors on the COUtilityCruiseCar object and its inner
objects (COCruiseCar and COCar). Eventually, the server's object count
(cObjects) is decremented to 0. The CServer::ObjectsDown method detects
this event and posts a WM_CLOSE message to the local server itself. This
message causes the server program to exit its message loop, and it calls
g_pServer->CloseFactories.

  L: CServer::CloseFactories. Begin.
  C: COUtilityCruiseCar::CImpIUtility Destructor.
  C: COUtilityCruiseCar::CImpICruise Destructor.
  C: COUtilityCruiseCar::CImpICar Destructor.
  L: CServer::CloseFactories. Revoke CFCar.
  L: CFCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCar::AddRef. New cRefs=3.
  L: CFCar::Release. New cRefs=2.
  L: CFCar::Release. New cRefs=1.
  L: CServer::CloseFactories. Revoke CFUtilityCar.
  L: CFUtilityCar::QueryInterface. 'this' pIUnknown returned.
  L: CFUtilityCar::AddRef. New cRefs=3.
  L: CFUtilityCar::Release. New cRefs=2.
  L: CFUtilityCar::Release. New cRefs=1.
  L: CServer::CloseFactories. Revoke CFCruiseCar.
  L: CFCruiseCar::QueryInterface. 'this' pIUnknown returned.
  L: CFCruiseCar::AddRef. New cRefs=3.
  L: CFCruiseCar::Release. New cRefs=2.
  L: CFCruiseCar::Release. New cRefs=1.
  L: CServer::CloseFactories. Release all factory interfaces.
  L: CFCar::Release. New cRefs=0.
  L: CFCar::Destructor.
  L: CFCar::CImpIClassFactory Destructor.
  L: CFUtilityCar::Release. New cRefs=0.
  L: CFUtilityCar::Destructor.
  L: CFUtilityCar::CImpIClassFactory Destructor.
  L: CFCruiseCar::Release. New cRefs=0.
  L: CFCruiseCar::Destructor.
  L: CFCruiseCar::CImpIClassFactory Destructor.
  L: CServer::CloseFactories. End.
  L: Exiting LOCSERVE local server application.

CServer::CloseFactories makes a series of calls to COM (by means of
CoRevokeClassObject) to revoke registration of the class factories for
CFCar, CFUtilityCar, and CFCruiseCar.  We see some resultant calls to
Release on these class factories by COM itself--in each case leaving a
final reference count on the class factory of 1. CloseFactories then
calls Release on the remaining class factory pointers that the client
holds, causing reference counts to reach 0, at which time the the class
factory COM objects are destroyed. The LOCSERVE server application is
finally exited and is unloaded by Windows.

One final note about logging. You can run the LOCSERVE server in a
visible state, in which case it will log its internal behavior to its own
log display. In this way both LOCCLIEN and LOCSERVE can be visible on
screen at the same time, with separate log displays of their own activity.
Here's how to do it: Run LOCSERVE first (before LOCCLIEN) with the
-Embedding command line switch, and then run LOCCLIEN. Both the LOCSERVE
server and the LOCCLIEN client will be present on screen at the same time.
Each running EXE will log its internal behavior to its own separate
logging display.
