home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / tutsamp / aptserve / server.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-05  |  20.2 KB  |  656 lines

  1. /*+==========================================================================
  2.   File:      SERVER.CPP
  3.  
  4.   Summary:   Implementation file for the CServer server control utility
  5.              C++ object.  This object encapsulates the server's internal
  6.              control of global server object and lock counts to govern
  7.              server lifetime.
  8.  
  9.              For a comprehensive tutorial code tour of this module's
  10.              contents and offerings see the tutorial APTSERVE.HTM file.
  11.              For more specific technical details on the internal workings
  12.              see the comments dispersed throughout the module's source code.
  13.  
  14.   Classes:   CServer.
  15.  
  16.   Functions: .
  17.  
  18.   Origin:    1-17-97: atrent - Editor-inheritance from SERVER.CPP in
  19.                the LOCSERVE Tutorial Code Sample. [Revised]
  20.  
  21. ----------------------------------------------------------------------------
  22.   This file is part of the Microsoft COM Tutorial Code Samples.
  23.  
  24.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  25.  
  26.   This source code is intended only as a supplement to Microsoft
  27.   Development Tools and/or on-line documentation.  See these other
  28.   materials for detailed information regarding Microsoft code samples.
  29.  
  30.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  31.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  32.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  33.   PARTICULAR PURPOSE.
  34. ==========================================================================+*/
  35.  
  36. /*---------------------------------------------------------------------------
  37.   We include WINDOWS.H for all Win32 applications.
  38.   We include OLE2.H because we will make calls to the COM/OLE Libraries.
  39.   We include PROCESS.H because we will be using the C-Runtime's
  40.     _beginthreadex function.
  41.   We include APPUTIL.H because we will be building this DLL using
  42.     the convenient Virtual Window and Dialog classes and other
  43.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  44.   We include MICARS.H and CARGUIDS.H for the common car-related Interface
  45.     class, GUID, and CLSID specifications.
  46.   We include SERVER.H for the object class declarations for the
  47.     C++ CServer server control object.
  48.   We include FACTORY.H because it has the necessary internal class factory
  49.     declarations for this component server.
  50. ---------------------------------------------------------------------------*/
  51. #include <windows.h>
  52. #include <ole2.h>
  53. #include <process.h>
  54. #include <apputil.h>
  55. #include <micars.h>
  56. #include <carguids.h>
  57. #include "server.h"
  58. #include "factory.h"
  59.  
  60.  
  61. /*---------------------------------------------------------------------------
  62.   Implementation the internal CServer C++ object.  Used to encapsulate
  63.   global server data and the methods for Lock and Object count incrementing
  64.   and decrementing.
  65. ---------------------------------------------------------------------------*/
  66.  
  67. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  68.   Method:   CServer::CServer
  69.  
  70.   Summary:  CServer Constructor.
  71.  
  72.   Args:     void
  73.  
  74.   Modifies: lots-o-stuff.
  75.  
  76.   Returns:  void
  77. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  78. CServer::CServer(void)
  79. {
  80.   // Zero the Object and Lock counts for this server.
  81.   m_cObjects = 0;
  82.   m_cLocks = 0;
  83.  
  84.   // Zero the cached handles.
  85.   m_hInstServer = NULL;
  86.   m_hWndServer = NULL;
  87.  
  88.   // Zero the Factory and Apartment thread references.
  89.   m_pCFCar = NULL;
  90.   m_pCFUtilityCar = NULL;
  91.   m_pCFCruiseCar = NULL;
  92.   m_paiAptCar = NULL;
  93.   m_paiAptUtilityCar = NULL;
  94.   m_paiAptCruiseCar = NULL;
  95.   m_dwAptCar = 0;
  96.   m_dwAptUtilityCar = 0;
  97.   m_dwAptCruiseCar = 0;
  98.  
  99.   // NULL the apartment handle array
  100.   for (UINT i = 0; i<NUM_APARTMENTS; i++)
  101.     m_hApts[i] = NULL;
  102.  
  103.   return;
  104. }
  105.  
  106.  
  107. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  108.   Method:   CServer::~CServer
  109.  
  110.   Summary:  CServer Destructor.
  111.  
  112.   Args:     void
  113.  
  114.   Modifies: .
  115.  
  116.   Returns:  void
  117. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  118. CServer::~CServer(void)
  119. {
  120.  
  121.   return;
  122. }
  123.  
  124.  
  125. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  126.   Method:   CServer::OwnThis
  127.  
  128.   Summary:  Wait for multithread exclusive ownership of this CServer object.
  129.             This definition overrides the virtual function defined in the
  130.             CThreaded base class to permit convenient trace logging here.
  131.  
  132.   Args:     void
  133.  
  134.   Modifies: m_bOwned.
  135.  
  136.   Returns:  BOOL
  137.               TRUE if success; FALSE if not.
  138. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  139. BOOL CServer::OwnThis(void)
  140. {
  141.   BOOL bOwned = FALSE;
  142.  
  143.   LOGF1("L: CServer::OwnThis. Thread <%X> waiting to own CServer.",TID);
  144.  
  145.   if (WAIT_OBJECT_0 == WaitForSingleObject(m_hOwnerMutex, INFINITE))
  146.   {
  147.     m_bOwned = bOwned = TRUE;
  148.     LOGF1("L: CServer::OwnThis. CServer now owned by Thread <%X>.",TID);
  149.   }
  150.  
  151.   return bOwned;
  152. }
  153.  
  154.  
  155. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  156.   Method:   CServer::UnOwnThis
  157.  
  158.   Summary:  Relinquish ownership of this CServer object.
  159.             This definition overrides the virtual function defined in the
  160.             CThreaded base class to permit convenient trace logging here.
  161.  
  162.   Args:     void
  163.  
  164.   Modifies: m_bOwned.
  165.  
  166.   Returns:  void
  167. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  168. void CServer::UnOwnThis(void)
  169. {
  170.   if (m_bOwned)
  171.   {
  172.     LOGF1("L: CServer::UnOwnThis. Ownership relinquished by <%X>.",TID);
  173.     m_bOwned = FALSE;
  174.     ReleaseMutex(m_hOwnerMutex);
  175.   }
  176.  
  177.   return;
  178. }
  179.  
  180.  
  181. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  182.   Method:   CServer::ObjectsUp
  183.  
  184.   Summary:  Increment the Server's living Object count.
  185.  
  186.   Args:     void
  187.  
  188.   Modifies: m_cObjects.
  189.  
  190.   Returns:  void
  191. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  192. void CServer::ObjectsUp(void)
  193. {
  194.   if (OwnThis())
  195.   {
  196.     m_cObjects += 1;
  197.     LOGF2("L<%X>: CServer::ObjectsUp. New cObjects=%i.",TID,m_cObjects);
  198.  
  199.     UnOwnThis();
  200.   }
  201.  
  202.   return;
  203. }
  204.  
  205.  
  206. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  207.   Method:   CServer::ObjectsDown
  208.  
  209.   Summary:  Decrement the Server's living object count. Trigger an unload
  210.             of this entire server if no more living components.
  211.  
  212.   Args:     void
  213.  
  214.   Modifies: m_cObjects.
  215.  
  216.   Returns:  void
  217. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  218. void CServer::ObjectsDown(void)
  219. {
  220.   if (OwnThis())
  221.   {
  222.     if (m_cObjects > 0)
  223.       m_cObjects -= 1;
  224.  
  225.     LOGF2("L<%X>: CServer::ObjectsDown. New cObjects=%i.",TID,m_cObjects);
  226.  
  227.     // If no more living objects and no locks then shut down the server.
  228.     if (0L == m_cObjects && 0L == m_cLocks && IsWindow(m_hWndServer))
  229.     {
  230.       LOGF1("L<%X>: CServer::ObjectsDown. Closing down APTSERVE server.",TID);
  231.  
  232.       // Relinquish current thread ownership of CServer before signaling
  233.       // the main server thread to close down the entire server. During
  234.       // shutdown other threads may need to access CServer.
  235.       UnOwnThis();
  236.  
  237.       // Post a message to this local server's message queue requesting
  238.       // a close of the entire server application. This will force a
  239.       // termination of all apartment threads (including the one that
  240.       // may be executing this ObjectsDown)
  241.       PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);
  242.     }
  243.     else
  244.       UnOwnThis();
  245.   }
  246.  
  247.   return;
  248. }
  249.  
  250.  
  251. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  252.   Method:   CServer::Lock
  253.  
  254.   Summary:  Increment the Server's Lock count.
  255.  
  256.   Args:     void
  257.  
  258.   Modifies: m_cLocks.
  259.  
  260.   Returns:  void
  261. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  262. void CServer::Lock(void)
  263. {
  264.   if (OwnThis())
  265.   {
  266.     m_cLocks += 1;
  267.  
  268.     LOGF2("L<%X>: CServer::Lock. New cLocks=%i.",TID,m_cLocks);
  269.  
  270.     UnOwnThis();
  271.   }
  272.  
  273.   return;
  274. }
  275.  
  276.  
  277. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  278.   Method:   CServer::Unlock
  279.  
  280.   Summary:  Decrement the Server's Lock count.
  281.  
  282.   Args:     void
  283.  
  284.   Modifies: m_cLocks.
  285.  
  286.   Returns:  void
  287. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  288. void CServer::Unlock(void)
  289. {
  290.   if (OwnThis())
  291.   {
  292.     m_cLocks -= 1;
  293.     if (m_cLocks < 0)
  294.       m_cLocks = 0;
  295.  
  296.     LOGF2("L<%X>: CServer::Unlock. New cLocks=%i.",TID,m_cLocks);
  297.  
  298.     // If no more living objects and no locks then shut down the server.
  299.     if (0L == m_cObjects && 0L == m_cLocks && IsWindow(m_hWndServer))
  300.     {
  301.       LOGF1("L<%X>: CServer::Unlock. Closing down APTSERVE server.",TID);
  302.  
  303.       // Relinquish current thread ownership of CServer before signaling
  304.       // the main server thread to close down the entire server. During
  305.       // shutdown other threads may need to access CServer.
  306.       UnOwnThis();
  307.  
  308.       // Post a message to this local server's message queue requesting
  309.       // a close of the entire server application. This will force a
  310.       // termination of all apartment threads (including the one that
  311.       // may be executing this ObjectsDown)
  312.       PostMessage(m_hWndServer, WM_CLOSE, 0, 0L);
  313.     }
  314.     else
  315.       UnOwnThis();
  316.   }
  317.  
  318.   return;
  319. }
  320.  
  321.  
  322. /*F+F++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  323.   Function: StartThread
  324.  
  325.   Summary:  This local function starts a new execution thread using the
  326.             C-RunTime's _beginthreadex function which takes care of
  327.             necessary business (that the Win32 CreateThread does not) if
  328.             the thread will make calls to C-Runtime functions--a likely
  329.             possibility.
  330.  
  331.   Args:     PCRTTHREADPROC pThreadProc,
  332.               Address of the Thread Procedure to start executing.
  333.             LPVOID pInitData,
  334.               Address of a structure of initialization data. Instead of
  335.               containing an address, this parameter can also contain a
  336.               single DWORD of data.
  337.             DWORD* pdwThreadId);
  338.               Address of a DWORD variable that will receive the
  339.               thread ID of the new thread.
  340.  
  341.   Returns:  void.
  342. ------------------------------------------------------------------------F-F*/
  343. HANDLE StartThread(
  344.          PCRTTHREADPROC pThreadProc,
  345.          LPVOID pInitData,
  346.          DWORD* pdwThreadId)
  347. {
  348.   HANDLE hThrd;
  349.  
  350.   hThrd = (HANDLE) _beginthreadex(
  351.                      0,                       // Default security specified.
  352.                      0,                       // Default stack size.
  353.                      pThreadProc,             // Thread Procedure address.
  354.                      (LPVOID) pInitData,      // Address of Init data.
  355.                      0,                       // Default state (running).
  356.                      (unsigned*)pdwThreadId); // Address of ThreadID Var.
  357.  
  358.   return hThrd;
  359. }
  360.  
  361.  
  362. /*F+F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F+++F
  363.   Function: AptThreadProc
  364.  
  365.   Summary:  The common apartment model thread procedure for this server.
  366.  
  367.   Args:     LPARAM lparam
  368.               Standard Window Proc parameter.
  369.  
  370.   Modifies: .
  371.  
  372.   Returns:  unsigned
  373.               Thread procedure return (usually msg.wParam).
  374. F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F---F-F*/
  375. unsigned WINAPI AptThreadProc(
  376.                   LPARAM lparam)
  377. {
  378.   HRESULT hr;
  379.   MSG msg;
  380.   DWORD dwCFRegId;
  381.   APT_INIT_DATA* paid = (APT_INIT_DATA*) lparam;
  382.  
  383.   LOGF1("L: AptThreadProc. Starting Apartment Thread <%X>.",TID);
  384.  
  385.   // Initialize COM for this apartment thread. Default of apartment
  386.   // model is assumed.
  387.   hr = CoInitialize(NULL);
  388.  
  389.   // Now register the class factory with COM.
  390.   LOGF1("L: AptThreadProc. Registering class factory of apartment <%X>.",TID);
  391.   hr = CoRegisterClassObject(
  392.          paid->rclsid,
  393.          paid->pcf,
  394.          CLSCTX_LOCAL_SERVER,
  395.          REGCLS_MULTIPLEUSE,
  396.          &dwCFRegId);
  397.   LOGERROR("L:CoRegisterClassObject",hr);
  398.   if (SUCCEEDED(hr))
  399.   {
  400.     // Provide a message pump for this thread.
  401.     while (GetMessage(&msg, 0, 0, 0))
  402.       DispatchMessage(&msg);
  403.  
  404.     LOGF1("L: AptThreadProc. Revoking class factory of apartment <%X>.",TID);
  405.     // Unregister the class factory with COM when the thread dies.
  406.     CoRevokeClassObject(dwCFRegId);
  407.   }
  408.   else
  409.   {
  410.     LOGF2("L<%X>: AptThreadProc. RegisterClass failed. hr=0x%X.",TID,hr);
  411.   }
  412.  
  413.   // Uninitialize COM in the context of this apartment thread.
  414.   CoUninitialize();
  415.  
  416.   LOGF1("L: AptThreadProc. Apartment Thread <%X> Terminated.",TID);
  417.  
  418.   return msg.wParam;
  419. }
  420.  
  421.  
  422. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  423.   Method:   CServer::OpenFactories
  424.  
  425.   Summary:  Create and register all of this server's class factories.
  426.  
  427.   Args:     void
  428.  
  429.   Modifies: See below.
  430.  
  431.   Returns:  BOOL
  432.               TRUE if success; FALSE if not
  433. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  434. BOOL CServer::OpenFactories(void)
  435. {
  436.   BOOL bOk = FALSE;
  437.   HRESULT hr;
  438.  
  439.   LOGF1("L<%X>: CServer::OpenFactories. Begin.",TID);
  440.  
  441.   if (OwnThis())
  442.   {
  443.     // Create the ClassFactory C++ objects.
  444.     m_pCFCar = new CFCar(NULL, this);
  445.     m_pCFUtilityCar = new CFUtilityCar(NULL, this);
  446.     m_pCFCruiseCar = new CFCruiseCar(NULL, this);
  447.  
  448.     // Create Structures for Apartment initialization.
  449.     m_paiAptCar = new APT_INIT_DATA(CLSID_AptCar);
  450.     m_paiAptUtilityCar = new APT_INIT_DATA(CLSID_AptUtilityCar);
  451.     m_paiAptCruiseCar = new APT_INIT_DATA(CLSID_AptCruiseCar);
  452.  
  453.     // Create the Appartment for AptCar.
  454.     LOGF1("L<%X>: CServer::OpenFactories. AptCar.",TID);
  455.     if (NULL != m_pCFCar && NULL != m_paiAptCar)
  456.     {
  457.       // AddRef this cached pointer to the Class Factory.
  458.       m_pCFCar->AddRef();
  459.  
  460.       // Assign the ClassFactory in the apartment init data and AddRef.
  461.       m_paiAptCar->pcf = m_pCFCar;
  462.       m_paiAptCar->pcf->AddRef();
  463.  
  464.       // Start the Apartment Thread using a local utility function.
  465.       m_hApts[APTCAR] = StartThread(
  466.                           (PCRTTHREADPROC) AptThreadProc,
  467.                           (LPVOID) m_paiAptCar,
  468.                           &m_dwAptCar);
  469.  
  470.       bOk = (NULL != m_hApts[APTCAR]);
  471.       if (!bOk)
  472.       {
  473.         hr = GetLastError();
  474.         LOGF2("L<%X>: CServer::OpenFactories. AptCar failed. hr=0x%X.",TID,hr);
  475.         // If can't register factory then clean up for server exit.
  476.         m_pCFCar->Release();
  477.         m_paiAptCar->pcf->Release();
  478.         DELETE_POINTER(m_pCFCar);
  479.         DELETE_POINTER(m_paiAptCar);
  480.       }
  481.     }
  482.     else
  483.       bOk = FALSE;
  484.  
  485.     // Create the Appartment for AptUtiliytCar.
  486.     LOGF1("L<%X>: CServer::OpenFactories. AptUtilityCar.",TID);
  487.     if (bOk)
  488.     {
  489.       if (NULL != m_pCFUtilityCar && NULL != m_paiAptUtilityCar)
  490.       {
  491.         // AddRef this cached pointer to the Class Factory.
  492.         m_pCFUtilityCar->AddRef();
  493.  
  494.         // Assign the ClassFactory in the apartment init data and AddRef again.
  495.         m_paiAptUtilityCar->pcf = m_pCFUtilityCar;
  496.         m_paiAptUtilityCar->pcf->AddRef();
  497.  
  498.         // Start the Apartment Thread using a local utility function.
  499.         m_hApts[APTUTILITYCAR] = StartThread(
  500.                                    (PCRTTHREADPROC) AptThreadProc,
  501.                                    (LPVOID) m_paiAptUtilityCar,
  502.                                    &m_dwAptUtilityCar);
  503.  
  504.         bOk = (NULL != m_hApts[APTUTILITYCAR]);
  505.         if (!bOk)
  506.         {
  507.           hr = GetLastError();
  508.           LOGF2("L<%X>: CServer::OpenFactories. AptUtilityCar failed. hr=0x%X.",TID,hr);
  509.           // If can't register factory then clean up for server exit.
  510.           m_pCFUtilityCar->Release();
  511.           m_paiAptUtilityCar->pcf->Release();
  512.           DELETE_POINTER(m_pCFUtilityCar);
  513.           DELETE_POINTER(m_paiAptUtilityCar);
  514.         }
  515.       }
  516.       else
  517.         bOk = FALSE;
  518.     }
  519.  
  520.     // Create the Appartment for AptCruiseCar.
  521.     LOGF1("L<%X>: CServer::OpenFactories. AptCruiseCar.",TID);
  522.     if (bOk)
  523.     {
  524.       if (NULL != m_pCFCruiseCar && NULL != m_paiAptCruiseCar)
  525.       {
  526.         // AddRef this cached pointer to the Class Factory.
  527.         m_pCFCruiseCar->AddRef();
  528.  
  529.         // Assign the ClassFactory in the apartment init data and AddRef again.
  530.         m_paiAptCruiseCar->pcf = m_pCFCruiseCar;
  531.         m_paiAptCruiseCar->pcf->AddRef();
  532.  
  533.         // Start the Apartment Thread using a local utility function.
  534.         m_hApts[APTCRUISECAR] = StartThread(
  535.                                   (PCRTTHREADPROC) AptThreadProc,
  536.                                   (LPVOID) m_paiAptCruiseCar,
  537.                                   &m_dwAptCruiseCar);
  538.  
  539.         bOk = (NULL != m_hApts[APTCRUISECAR]);
  540.         if (!bOk)
  541.         {
  542.           hr = GetLastError();
  543.           LOGF2("L<%X>: CServer::OpenFactories. AptCruiseCar failed. hr=0x%X.",TID,hr);
  544.           // If can't register factory then clean up for server exit.
  545.           m_pCFCruiseCar->Release();
  546.           m_paiAptCruiseCar->pcf->Release();
  547.           DELETE_POINTER(m_pCFCruiseCar);
  548.           DELETE_POINTER(m_paiAptCruiseCar);
  549.         }
  550.       }
  551.       else
  552.         bOk = FALSE;
  553.     }
  554.  
  555.     UnOwnThis();
  556.   }
  557.  
  558.   LOGF1("L<%X>: CServer::OpenFactories. End.",TID);
  559.  
  560.   return bOk;
  561. }
  562.  
  563.  
  564. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  565.   Method:   CServer::CloseFactories
  566.  
  567.   Summary:  Shutdown the class factory apartments. Revoke (ie, unregister)
  568.             and delete all the server's class factories too.
  569.  
  570.   Args:     void
  571.  
  572.   Modifies: .
  573.  
  574.   Returns:  BOOL
  575.               TRUE if success; FALSE if not
  576. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  577. BOOL CServer::CloseFactories(void)
  578. {
  579.   BOOL bOk = TRUE;
  580.   HRESULT hr;
  581.  
  582.   LOGF1("L<%X>: CServer::CloseFactories. Begin.",TID);
  583.  
  584.   if (OwnThis())
  585.   {
  586.     // Shutdown the AptCar Apartment Thread.
  587.     if (0 != m_dwAptCar)
  588.     {
  589.       LOGF1("L<%X>: CServer::CloseFactories. Terminate AptCar Apartment.",TID);
  590.       bOk = PostThreadMessage(m_dwAptCar, WM_QUIT, 0, 0);
  591.       if (!bOk)
  592.       {
  593.         hr = GetLastError();
  594.         LOGF2("L<%X>: CServer::CloseFactories. AptCar failed. hr=0x%X.",TID,hr);
  595.       }
  596.     }
  597.  
  598.     // Shutdown the AptUtilityCar Apartment Thread.
  599.     if (0 != m_dwAptUtilityCar)
  600.     {
  601.       LOGF1("L<%X>: CServer::CloseFactories. Terminate AptUtilityCar Apartment.",TID);
  602.       bOk = PostThreadMessage(m_dwAptUtilityCar, WM_QUIT, 0, 0);
  603.       if (!bOk)
  604.       {
  605.         hr = GetLastError();
  606.         LOGF2("L<%X>: CServer::CloseFactories. AptUtilityCar failed. hr=0x%X.",TID,hr);
  607.       }
  608.     }
  609.  
  610.     // Shutdown the AptCruiseCar Apartment Thread.
  611.     if (0 != m_dwAptCruiseCar)
  612.     {
  613.       LOGF1("L<%X>: CServer::CloseFactories. Terminate AptCruiseCar Apartment.",TID);
  614.       bOk = PostThreadMessage(m_dwAptCruiseCar, WM_QUIT, 0, 0);
  615.       if (!bOk)
  616.       {
  617.         hr = GetLastError();
  618.         LOGF2("L<%X>: CServer::CloseFactories. AptCruiseCar failed. hr=0x%X.",TID,hr);
  619.       }
  620.     }
  621.  
  622.     if (m_pCFCar && m_pCFUtilityCar && m_pCFCruiseCar)
  623.     {
  624.       // Release any and all of the Class Factory interface pointers.
  625.       LOGF1("L<%X>: CServer::CloseFactories. Releasing all Classfactory interfaces.",TID);
  626.       RELEASE_INTERFACE(m_pCFCar);
  627.       RELEASE_INTERFACE(m_paiAptCar->pcf);
  628.       RELEASE_INTERFACE(m_pCFUtilityCar);
  629.       RELEASE_INTERFACE(m_paiAptUtilityCar->pcf);
  630.       RELEASE_INTERFACE(m_pCFCruiseCar);
  631.       RELEASE_INTERFACE(m_paiAptCruiseCar->pcf);
  632.       DELETE_POINTER(m_paiAptCar);
  633.       DELETE_POINTER(m_paiAptUtilityCar);
  634.       DELETE_POINTER(m_paiAptCruiseCar);
  635.  
  636.       // Give CServer back before waiting on threads to die.
  637.       UnOwnThis();
  638.  
  639.       // Wait a very long time (in CPU terms) for the apartment threads
  640.       // to exit/terminate before closing their thread handles.
  641.       WaitForMultipleObjects(NUM_APARTMENTS, m_hApts, TRUE, LONG_WAIT);
  642.       for (UINT i = 0; i<NUM_APARTMENTS; i++)
  643.         if (NULL != m_hApts[i])
  644.           CloseHandle(m_hApts[i]);
  645.     }
  646.     else
  647.       UnOwnThis();
  648.   }
  649.  
  650.   LOGF1("L<%X>: CServer::CloseFactories. End.",TID);
  651.  
  652.   return bOk;
  653. }
  654.  
  655. // =============================== END ======================================
  656.