LOCSERVE - Local Server


SUMMARY
=======

The LOCSERVE sample begins with the car-related COM Objects of the previous
DLLSERVE sample and rehouses them in an out-of-process local server,
LOCSERVE.EXE. To do so requires little change to the COM objects themselves
(COCar, COUtilityCar, and COCruiseCar). This sample introduces the new
facilities to house them in a out-of-process COM server, including class
factories for each component.

This out-of-process server provides the following components: LocCar,
LocUtilityCar, and LocCruiseCar.

In the series of OLE tutorial code samples, LOCSERVE works with the
LOCCLIEN code sample to illustrate LOCSERVE's out-of-process local server
facilities for creating components that can be used by an EXE client and the
subsequent manipulation of those components by LOCCLIEN.EXE.

For functional descriptions and a tutorial code tour of LOCSERVE, see the
Code Tour section below. See also LOCCLIEN.TXT in the sibling \LOCCLIEN
directory for more details on the LOCCLIEN client application and how it
works with LOCSERVE.EXE itself. You must build LOCSERVE.EXE before building
or running LOCCLIEN. LOCSERVE's makefile automatically registers LOCSERVE's
components in the registry. These components must be registered before
LOCSERVE is available to outside COM clients as a server for those
components. This registration is done using the REGISTER.EXE utility built
in the earlier REGISTER lesson. To build or run LOCSERVE, you should build
the REGISTER code sample first.

As an out-of-process local server, LOCSERVE relies on standard marshaling
for clients to use its interfaces across process boundaries. Such standard
marshaling for the interfaces used in LOCSERVE's COM objects is provided
in the MARSHAL.DLL server built in the previous lesson. To build or run
LOCSERVE, you should build the MARSHAL code sample first.

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
-----

LOCSERVE is an application that is meant to be used as an out-of-process COM
server. Out-of-process servers like LOCSERVE are registered in the system
registry, and LOCSERVE has built-in support for registering its components.
It accepts the following command line switches to register and unregister:

    -RegServer or /RegServer to register
    -UnregServer or /UnregServer to unregister

String matches on these switches are case-insensitive. As an out-of-process
server, LOCSERVE also recognizes the standard -Embedding or /Embedding
switch, which directs it to run as such a server. In this sample, that means
LOCSERVE will run hidden. If you attempt to run LOCSERVE as a stand-alone
application, it will exit with an error. You can manually direct LOCSERVE to
run visible by starting it with an explicit -Embedding switch on its command
line prior to running the LOCCLIEN client.


CODE TOUR
=========

Files        Description

LOCSERVE.TXT This file.
MAKEFILE     The generic makefile for building the LOCSERVE.EXE
             code sample of this tutorial lesson.
LOCSERVE.H   The include file for the LOCSERVE application. Contains
             class declarations, function prototypes, and resource
             identifiers.
LOCSERVE.CPP The main implementation file for LOCSERVE.EXE. Has WinMain
             and CMainWindow implementation, as well as the main menu
             dispatching.
LOCSERVE.RC  The resource definition file for the executable.
LOCSERVE.ICO The icon resource for the executable.
SERVER.H     The include file for the server control C++ object. Also
             used for LOCSERVE externs.
SERVER.CPP   The implementation file for the server control object.
             Manages object counts and creation of class factories.
FACTORY.H    The include file for the server's class factory COM objects.
FACTORY.CPP  The implementation file for the server's class factories.
CAR.H        The include file for the COCar COM object class.
CAR.CPP      The implementation file for the COCar COM object class.
UTILCAR.H    The include file for the COUtililtyCar COM object class.
UTILCAR.CPP  The implementation file for the COUtilityCar COM object class.
CRUCAR.H     The include file for the COCruiseCar COM object class.
CRUCAR.CPP   The implementation file for the COCruiseCar COM object class.

With this LOCSERVE code sample, we cross process boundaries for the first
time when the client manipulates components in the server. With DLLSERVE,
we saw a server that was registered as in-process and was loaded by COM on
behalf of clients and unloaded by COM when no longer needed by any clients.
We saw the in-process server housing in DLLSERVE.DLL and how that housing
implemented and exposed its class factories.

With LOCSERVE, these server housing schemes are different. As a separate
EXE application with its own message loop, the server itself must take on
more responsibility for its lifetime and must expose its class factories
to COM differently.

Though not essential to the COM nature of this out-of-process local server,
the logging facility that we used previously to enhance the tutorial value
of these code samples needs significant changes to accommodate
cross-process trace logging from the server to the client.

In a general sense, LOCSERVE is an EXE version of DLLSERVE. As COM servers,
they both offer the same components: COCar, COUtilityCar, and COCruiseCar.
They both also provide appropriate class factories for those components:
CFCar, CFUtilityCar, and CFCruiseCar.

The main point of this LOCSERVE code sample is to illustrate the
mechanisms required to render its COM objects into components by housing
them in an out-of-process server.

Like its predecessors, LOCSERVE uses many of the utility classes and
services provided by APPUTIL. For more details on APPUTIL, study the
APPUTIL library's source code and APPUTIL.TXT, located in the sibling
\APPUTIL directory.

Also like its server predecessors, LOCSERVE is self-registering, and its
makefile uses REGISTER.EXE (built in the REGISTER lesson) to register
LOCSERVE. We'll look first at the support for this facility that is
provided in LOCSERVE. We will then see how and when the class factories
are built. We'll tour the mechanism for control of the server as objects
are created and deleted. We'll look at the changes made to the COM object
classes of the components themselves. Finally, we'll look at the
mechanism needed to log internal server behavior to the client's trace log
display.

We start in LOCSERVE.CPP with the following registration-related code in
the WinMain function:

    ...
    ...
    // Check command line for switches to register or unregister
    // this server's managed components. iRun will be set to 2
    // to signal an immediate and quiet exit of this application
    // if such registration or unregistration is requested.
    if (0 == lstrcmpiA(lpCmdLine, "-RegServer")
        || 0 == lstrcmpiA(lpCmdLine, "/RegServer"))
    {
      if (pWin->RegisterServer())
        iRun = 2;
    }
    else if (0 == lstrcmpiA(lpCmdLine, "-UnregServer")
             || 0 == lstrcmpiA(lpCmdLine, "/UnregServer"))
    {
      if (pWin->UnregisterServer())
        iRun = 2;
    }
    ...
    ...

WinMain has been toured in previous lessons. The logic above shows how an
out-of-process server can honor self-registration requests by recognizing
certain standard switches on its command line. The above code runs after
CMainWindow has been created, but before InitInstance is called and the
main message loop is entered. The code checks the command line (using
pointer lpCmdLine, passed with the call to WinMain) for the appropriate
switches. The explicit ANSI variants of lstrcmpi are used, because the
command line is available only in ANSI, even if this application is
compiled for Unicode.

The string comparisons are case-insensitive. If switch -RegServer was
specified on the command line, then CMainWindow::RegisterServer is called.
Similarly, if switch -UnregServer was specified,
CMainWindow::UnregisterServer is called. In the REGISTER code sample, we saw
how these switches might have been specified when REGISTER started
LOCSERVE.EXE with a call to WinExec. The switches can also be specified
manually if LOCSERVE is started in the Command Prompt window. After the
appropriate registration or unregistration function is called, an iRun
variable is set to 2 to indicate that LOCSERVE will be exited immediately
without entering its message loop.

Here is a portion of RegisterServer that registers the LocCar component:

  BOOL CMainWindow::RegisterServer(void)
  {
    BOOL  bOk = TRUE;
    TCHAR szID[GUID_SIZE+1];
    TCHAR szCLSID[GUID_SIZE+1];
    TCHAR szModulePath[MAX_PATH];

    // Obtain the path to this module's executable file for later use.
    GetModuleFileName(
      g_pServer->m_hInstServer,
      szModulePath,
      sizeof(szModulePath)/sizeof(TCHAR));

    /*---------------------------------------------------------------------
      Create registry entries for the LocCar Component.
    ---------------------------------------------------------------------*/
    // Create some base key strings.
    StringFromGUID2(CLSID_LocCar, szID, GUID_SIZE);
    lstrcpy(szCLSID, TEXT("CLSID\\"));
    lstrcat(szCLSID, szID);

    // Create ProgID keys.
    SetRegKeyValue(
      TEXT("LocCar1.0"),
      NULL,
      TEXT("LocCar Component - LOCSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("LocCar1.0"),
      TEXT("CLSID"),
      szID);

    // Create VersionIndependentProgID keys.
    SetRegKeyValue(
      TEXT("LocCar"),
      NULL,
      TEXT("LocCar Component - LOCSERVE Code Sample"));
    SetRegKeyValue(
      TEXT("LocCar"),
      TEXT("CurVer"),
      TEXT("LocCar1.0"));
    SetRegKeyValue(
      TEXT("LocCar"),
      TEXT("CLSID"),
      szID);

    // Create entries under CLSID.
    SetRegKeyValue(
      szCLSID,
      NULL,
      TEXT("LocCar Component - LOCSERVE Code Sample"));
    SetRegKeyValue(
      szCLSID,
      TEXT("ProgID"),
      TEXT("LocCar1.0"));
    SetRegKeyValue(
      szCLSID,
      TEXT("VersionIndependentProgID"),
      TEXT("LocCar"));
    SetRegKeyValue(
      szCLSID,
      TEXT("NotInsertable"),
      NULL);
    SetRegKeyValue(
      szCLSID,
      TEXT("LaunchPermission"),
      TEXT("Y"));
    SetRegKeyValue(
      szCLSID,
      TEXT("LocalServer32"),
      szModulePath);

  /*-------------------------------------------------------------------------
    Create registry entries for the LocUtilityCar Component.
  -------------------------------------------------------------------------*/
      ...
      ... Similar to above code for LocCar.
      ...

  /*-------------------------------------------------------------------------
    Create registry entries for the LocCruiseCar Component.
  -------------------------------------------------------------------------*/
      ...
      ... Similar to above code for LocCar.
      ...

    return bOk;
  }

This registration code is very similar to that used in DLLSERVE for its
DllCar component. The CLSID_LocCar (defined in CARGUIDS.H) is used for
this component. CARGUIDS.H is found in the sibling INC directory. The main
difference is the "LocalServer32" entry. After such registration code is
run (for example, as this server is built), you can run the Registry
Editor (REGEDT32.EXE in Windows NT, REGEDIT.EXE in Windows 95) and peruse
the registered entries. The registry entries look like this for the LocCar
component:

  HKEY_CLASSES_ROOT
    \CLSID
       \{0002DA0A-0000-0000-C000-000000000046} =
                              "LocCar Component - LOCSERVE Code Sample"
           \ProgID = "LocCar1.0"
           \VersionIndependentProgID = "LocCar"
           \NotInsertable
           \LaunchPermission = "Y"
           \LocalServer32 = "G:\WORK\TUTORIAL\LOCSERVE\LOCSERVE.EXE"
    ...
    ...
    \LocCar = "LocCar Component - LOCSERVE Code Sample"
       \CurVer = "LocCar1.0"
       \CLSID = "{0002DA0A-0000-0000-C000-000000000046}"
    ...
    ...
    \LocCar1.0 ="LocCar Component - LOCSERVE Code Sample"
       \CLSID = "{0002DA0A-0000-0000-C000-000000000046}"

Separate entries under HKEY_CLASSES_ROOT are written for the ProgID
(LocCar1.0) and the VersionIndependentProgID (LocCar). Both refer
unambiguously to the main HKEY_CLASSES_ROOT\CLSID entry. Under this entry
are indications that the server is not insertable as an object in an OLE
compound document. The LocalServer32 entry has the path location of the
server's executable. COM can thus start this server on behalf of a client
when given the right CLSID.

The UnregisterServer call, also in LOCSERVE.CPP, simply removes from the
registry all the entries that are written by RegisterServer.

The LOCSERVE application does have a skeleton menu in its main window. As
a hidden server, it doesn't need a menu at all, but it has one here for
debugging and tutorial purposes. For example, CMainWindow::DoMenu honors
the IDM_HELP_ABOUT case. In previous code samples, the server could be
directed to show its About dialog box from the client. This is not of
major importance, but we illustrate it here by having the client send a
message to LOCSERVE's main window. More on this in the LOCCLIEN code
tour.

When LOCSERVE is started by COM on behalf of a client, COM uses a standard
-Embedding command line switch to notify the server that it has been
invoked as a server. Code in the WinMain function handles this switch and
sets up LOCSERVE as a server of its components. Here is a code fragment
from WinMain (in file LOCSERVE.CPP):

      ...
      ...
      if (FALSE == iRun)
      {
        // If we did not process a command line switch that
        // requires immediate exit, then initialize an instance of
        // the new CMainWindow. This entails creating the main window.
        // Note: if InitInstance fails, then it would have already
        // deleted pWin so we wouldn't need to delete it here.
        if (pWin->InitInstance(hInstance, nCmdShow))
        {
          // Create and register the Class Factories.  But only do so
          // if this application has been started by COM as indicated
          // by the -Embedding command line switch.
          if (0 == lstrcmpiA(lpCmdLine, "-Embedding")
              || 0 == lstrcmpiA(lpCmdLine, "/Embedding"))
            iRun = g_pServer->OpenFactories();
        }
      }
      ...
      ...

In addition to creating the main window, InitInstance also sets up logging
from this server to the LOCCLIEN client's trace log display. More on this
later.

The important thing now is the OpenFactories call above: it is called only
if the -Embedding switch is detected. Regardless of whether this
application was compiled for Unicode, the command line is always ANSI, so
the ANSI-only version of the lstrcmpi string utility function is used.
The g_pServer pointer points to this server's CServer server control
object. For this out-of-process server, CServe has two important new
methods that it did not have in DLLSERVE. One, OpenFactories, creates and
registers all the class factories. The other, CloseFactories, shuts them
all down.

We see above when OpenFactories is called. Its matching CloseFactories
call is made in CMainWindow's destructor in LOCSERVE.CPP.

  ...
  // Close down the factories (ie, Revoke and release the Class Factories).
  if (NULL != g_pServer)
    g_pServer->CloseFactories();
  ...

The shutdown sequence is initiated by the server itself on the basis of
the object and lock counts. For example, see the listing below of the
ObjectsDown method of CServer. ObjectsDown detects when there are no
longer any existing COM objects or lock counts. If there are none it
issues the following call causing an eventual execution of CMainWindow's
destructor.

  PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);

When the application's main window is closed in response to the WM_CLOSE
message, the main window procedure is called with the WM_DESTROY message.
This message is sent when the window is being destroyed in response to a
close of the window. Because CMainWindow is derived from APPUTIL's
CVirWindow, WM_DESTROY is trapped by CVirWindow's WindowProc function
where a delete of CMainWindow is performed. This runs the CMainWindow
destructor which calls CloseFactories as above. After CloseFactories shuts
down all the class factories, the destructor finally posts a WM_QUIT
message to the server application's main thread and causes the exit of the
thread's message loop. After this the server application is exited.

We will tour the class factory code in more detail. CServer is used to
manage creation and destruction of the class factories. Here is the
CServe declaration in SERVER.H.

  class CServer
  {
    public:
      CServer(void);
      ~CServer(void);

      void Lock(void);
      void Unlock(void);
      void ObjectsUp(void);
      void ObjectsDown(void);
      BOOL OpenFactories(void);
      BOOL CloseFactories(void);

      // A place to store the server's instance handle.
      HINSTANCE m_hInstServer;

      // A place to store the server's main window.
      HINSTANCE m_hWndServer;

      // Global DLL Server living Object count.
      LONG m_cObjects;

      // Global DLL Server Client Lock count.
      LONG m_cLocks;

      // Some member variables to store pointers to Class Factories.
      IUnknown* m_pCFCar;
      IUnknown* m_pCFUtilityCar;
      IUnknown* m_pCFCruiseCar;

      // Some member variables to store Class Factory registration keys.
      DWORD m_dwCFCar;
      DWORD m_dwCFUtilityCar;
      DWORD m_dwCFCruiseCar;
  };

Much of this code is the same as that in previous servers in this sample
series. We see the prototypes for the OpenFactories and CloseFactories
methods. We notice storage for pointers to the IUnknowns of the three
class factories (m_pCFCar, m_pCFUtilityCar, m_pCFCruiseCAr) and storage
for registration keys for the class factories (m_dwCFCar,
m_dwCFUtilityCar, m_dwCFCruiseCar). This registration is different from
writing entries in the system registry. The class factories are
registered with COM at run-time, and COM provides a key or token for each
registered class factory. Each token is later used to revoke the
registration with COM for each class factory. The registration code
informs COM that a class factory exists. Because the client has no direct
way to call the server to create a class factory on demand, an
out-of-process server must create them all during its initialization and
destroy them all just before exiting.

Here is OpenFactories in SERVER.CPP:

  BOOL CServer::OpenFactories(void)
  {
    BOOL bOk = FALSE;
    HRESULT hr;

    LOG("L: CServer::OpenFactories. Begin.");

    // Build and register the LocCar factory.
    m_pCFCar = new CFCar(NULL, this);
    if (NULL != m_pCFCar)
    {
      // AddRef this cached pointer to the Class Factory.
      m_pCFCar->AddRef();

      // Now register this class factory with COM.
      hr = CoRegisterClassObject(
             CLSID_LocCar,
             m_pCFCar,
             CLSCTX_LOCAL_SERVER,
             REGCLS_MULTIPLEUSE,
             &m_dwCFCar);

      bOk = SUCCEEDED(hr);
      if (!bOk)
      {
        LOGF1("L: CServer::OpenFactories. CFCar failed. hr=0x%X.", hr);
        // If can't register factory then clean up for server exit.
        m_pCFCar->Release();
        DELETE_POINTER(m_pCFCar);
      }
    }
    else
      bOk = FALSE;

    ...
    ... <Similar for the LocUtilityCar and LocCruiseCar class factories.>
    ...

    LOG("L: CServer::OpenFactories. End.");

    return bOk;
  }

The CFCar COM object is created, and the m_pCFCar pointer is assigned. As
usual, AddRef must be called on this cached pointer. COM's
CoRegisterClassObject function is then called to register the new class
factory. It deposits a registration key in the CServer::m_dwCFCar member
variable, which is used later to revoke the CFCar class factory
registration with COM. The CLSCTX_LOCAL_SERVER argument informs COM that
the execution context of this class factory is that of an out-of-process
local server. The REGCLS_MULTIPLEUSE argument informs COM that this
server is multiple use for any given class factory. This means that COM
doesn't need to start a new instance of the server when another client
requests the same class factory. This is one reason that all the class
factories are created and registered during server initialization. If
they aren't created and registered in the beginning, there would be no way
to create a requested class factory that isn't already created, because
the server would not be restarted by COM. Once the class factory is
registered with COM, it has a pointer to the class factory object's
IUnknown. It can then use this pointer to make requests to the class
factory on behalf of such client requests as CoCreateInstance.

Here is the matching CloseFactories method call in SERVER.CPP.

  BOOL CServer::CloseFactories(void)
  {
    BOOL bOk = TRUE;
    HRESULT hr;

    LOG("L: CServer::CloseFactories. Begin.");

    // Unregister the LocCar class factory with COM.
    if (0 != m_dwCFCar)
    {
      LOG("L: CServer::CloseFactories. Revoke CFCar.");
      hr = CoRevokeClassObject(m_dwCFCar);
      if (FAILED(hr))
        bOk = FALSE;
    }

    ...
    ... <Similar for the LocUtilityCar and LocCruiseCar class factories.
    ...

    // Release any and all of the Class Factory interface pointers.
    LOG("L: CServer::CloseFactories. Release all factory interfaces.");
    RELEASE_INTERFACE(m_pCFCar);
    RELEASE_INTERFACE(m_pCFUtilityCar);
    RELEASE_INTERFACE(m_pCFCruiseCar);

    LOG("L: CServer::CloseFactories. End.");

    return bOk;
  }

The stored registration key is used to revoke the class factory
registration with COM. After that is successful for all factories, the
outstanding references to the factories are released. Each release that
decrements the reference count to 0 causes the associated class factory
object to be deleted.

Before we leave the CServer server control object, here is the ObjectsDown
method, also in SERVER.CPP:

  void CServer::ObjectsDown(void)
  {
    InterlockedDecrement((PLONG) &m_cObjects);

    LOGF1("L: CServer::ObjectsDown. New cObjects=%i.", m_cObjects);

    // If no more living objects and no locks then shut the server down.
    if (0L == m_cObjects && 0L == m_cLocks && IsWindow(m_hWndServer))
    {
      LOG("L: CServer::ObjectsDown. Closing down LOCSERVE server.");
      // Post a message to this local server's message queue requesting
      // a close of the application.
      PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);
    }

    return;
  }

Here the server must take an active role in its own lifetime. When the
object and lock counts are 0, the server control object calls PostMessage
on the server application itself to send a WM_CLOSE messsage to the
application.

A curious twist of logic is also needed in the Unlock method in SERVER.CPP:

  void CServer::Unlock(void)
  {
    InterlockedDecrement((PLONG) &m_cLocks);

    LOGF1("L: CServer::Unlock. New cLocks=%i.", m_cLocks);

    // Use ObjectsDown to force a server shutdown if this warranted.
    InterlockedIncrement((PLONG) &m_cObjects);
    ObjectsDown();

    return;
  }

If the first decrement caused the lock count to reach zero, this event
might shut down the server. To trigger the shutdown, the m_cObjects count
is artifically incremented, and then ObjectsDown is called. We saw in the
ObjectsDown logic earlier that if the object count transitions to 0, a
WM_CLOSE message is sent to the server.

The code for the COCar, COUtilityCar, and COCruiseCar has been carried
over from DLLSERVE almost unchanged. One significant change was in the
COUtilityCar nested component. When its reused subordinate COCar object is
created using calls to COM's CoCreateInstance, the execution context is
changed from CLSCTX_INPROC_SERVER to CLSCTX_LOCAL_SERVER. Here's an
example from the creation of COUtilityCar from UTILCAR.CPP:

  ...
  ...
  // We create an instance of the COCar object and do this via the
  // Containment reuse technique. We ask for the new COM object's
  // ICar interface directly. We pass NULL for the pUnkOuter
  // aggregation pointer because we are not aggregating. It is here
  // that we are reusing the COCar COM Object through the Containment
  // technique. We cache the requested ICar interface pointer in this
  // COUtilityCar COM object for later use. We don't need to AddRef
  // this interface because the CoCreateInstance will do this for us.
  hr = CoCreateInstance(
         CLSID_LocCar,
         NULL,
         CLSCTX_LOCAL_SERVER,
         IID_ICar,
         (PPVOID)&m_pICar);
  ...
  ...

Notice that the custom interface methods use short parameters. This is so
that the interface specification in MICARS.IDL satisfies the Microsoft
Interface Definition Language (MIDL) compiler, which will not create
marshaling code for hardware-dependent INT arguments.

Recall from the DLLSERVE sample that COCruiseCar was constructed by
reusing the COCar COM object by aggregation. In this present LOCSERVE
local server we must retain the CLSCTX_INPROC_SERVER execution context for
the creation of the aggregated COCar. Here is the creation of
COCruiseCar's aggregated COCar object from CRUCAR.CPP.

  ...
  ...
  // We create an instance of the COCar object and do this via the
  // Aggregation reuse technique.  Note we pass pUnkOuter as the
  // Aggregation pointer.  It is the 'this' pointer to this present
  // CruiseCar object if we are not being aggregated; otherwise it is the
  // pointer to the outermost object's controlling IUnknown.  Following
  // the rules of Aggregation we must ask for an IID_IUnknown interface.
  // We cache the requested  pointer to the IUnknown of the new COCar COM
  // object for later use in delegating IUnknown calls. Since we know that
  // this LocCar component is housed in this very own server, we can
  // specify an execution context of CLSCTX_INPROC_SERVER. This allows us
  // to aggregate the COCar COM object. Though the object is in a local
  // server, it will be instantiated and run within the same process of
  // this present local server. If we specify CLSCTX_LOCAL_SERVER, COM
  // will not currently permit the creation using aggregation across
  // process boundaries and CoCreateInstance would return an error.
  hr = CoCreateInstance(
         CLSID_LocCar,
         pUnkOuter,
         CLSCTX_INPROC_SERVER,
         IID_IUnknown,
         (PPVOID)&m_pUnkCar);
  ...
  ...

COCruiseCar is instantiating and running an aggregated COCar within the
process of this same server. If CLSCTX_LOCAL_SERVER were specified, the
creation call would request aggregation across process boundaries. This is
not currently supported by COM and CoCreateInstance would return an error.

Another change in the individual COM object implementation modules was to
include MICARS.H rather than the ICARS.H file included in earlier code
samples. We do this to better ensure consistency with the interface
specification in MICARS.IDL. By including MICARS.H in both out-of-process
server and client application modules, we ensure that the interface code
in these client/server applications corresponds exactly to the same
interfaces specified to MIDL for marshaling. MIDL generates our MICARS.H
include file, and we code our use of those interfaces using MICARS.H. We
could have continued to use the ICARS.H file, but because the content of
the two files would be decoupled, there would have been a greater chance
for a discrepancy between what is specified in MICARS.IDL and what is
specified in ICARS.H.

In previous servers, we used a CarSample component to log in-process
server activity to the client's trace log display. In this out-of-process
local server, however, we cannot, for example, pass a g_MsgLog pointer
from the client to the server and assume the two processes' address spaces
are the same. They are not. To enable LOCSERVE to log to LOCCLIEN, a kind
of interprocess communication is required. Win32 offers a convenient way
to send data from one process to another on the same machine if the
destination window handle is known. This is the WM_COPYDATA message.
This approach requires that LOCSERVE get the window handle of the client
and use it in a Win32 SendMessage call with the WM_COPYDATA message. The
data being sent is the display string to be logged.

Here is a portion of CMainWindow::InitInstance (in file LOCSERVE.CPP):

  ...
  ...
  // If the Client's Main window is found, then set up logging to it.
  hWnd = FindWindow(NULL, TEXT(CLIENT_WINDOW_TITLE));
  if (NULL != hWnd)
  {
    // Tell the CSendLog object that we are logging to client.
    m_pMsgLog->LogToServer(FALSE);
    // Assign the global MsgLog pointer.
    g_pMsgLog = m_pMsgLog;
    m_pMsgLog->SetClient(m_hInst, m_hWnd, hWnd);
    LOGID(IDS_LOGTO_CLIENT);
  }
  ...
  ...

When LOCSERVE is initialized, it calls the FindWindow function to
locate LOCCLIEN's main window. (It has special knowledge of the window
title). If it doesn't find the client, then the code (not shown) creates a
log display in the server itself. This kind of logging within the
application is the same as that seen in previous code samples and will not
be covered again here. Once LOCSERVE finds the client's main window
handle, it can send messages to it. If successful, the logging facility
is notified that LogToServer is FALSE, and logging is to the client. The
global g_pMsgLog variable is then assigned. The SetClient function stores
the destination window handle for the client. The g_pMsgLog is the global
pointer used by all the LOGxx macros. However, CMsgLog behaves differently
than it did in earlier lessons, so we define g_pMsgLog as:

    CSendLog* g_pMsgLog;

We are using a different log facility (CSendLog instead of CMsgLog),
implemented in APPUTIL, but it has same-named methods for the important
logging calls. In past samples, the LOGxx macros were used successfully
to call methods on a CMsgLog facility. Because the CSendLog facility
mirrors the functionality of CMsgLog (though internally it sends the log
messages across process boundaries to the client), the same LOGxx macros
perform virtually the same logging funcionality, even though they are
calling methods on a different CSendLog facility. Looking briefly in
\APPUTIL\APPUTIL.CPP, we find the following fragment at the heart of the
CSendLog::Msg logging method. And there is the SendMessage call using
WM_COPYDATA.

  ...
  ...
  cds.dwData = 0;
  cds.cbData = lstrlen(szMsg)+1;
  cds.lpData = szMsg;

  bResult = SendMessage(
              m_hWndReceiver,
              WM_COPYDATA,
              (WPARAM) m_hWndSender,
              (LPARAM) &cds);
  ...
  ...

The LOCCLIEN client executes the following when it recieves this
WM_COPYDATA message:

    ...
    case WM_COPYDATA:
      // We have been sent a trace log message from a server.
      // Log it to our own Client's display.
      {
        LPTSTR pszMsg = (LPTSTR)((COPYDATASTRUCT*)lParam)->lpData;
        g_pMsgLog->Msg(pszMsg);
        #if defined(DEBUG)
        // Bump to next line in the debugger output window.
        ::OutputDebugString(TEXT("\r\n"));
        #endif
      }
      break;
    ...

The server's log message string is recieved and sent to the client's own
MsgLog display.
