SERVER.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:20k
源码类别:

Windows编程

开发平台:

Visual C++

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