home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / com / freethrd / server / freserve.txt < prev    next >
Encoding:
Text File  |  1998-04-03  |  24.6 KB  |  555 lines

  1. FRESERVE - Free-threaded COM objects in an in-process server
  2.  
  3.  
  4. SUMMARY
  5. =======
  6.  
  7. The FRESERVE sample shows how to construct a COM object in a free-threaded
  8. in-process server. This sample departs from the sport utility vehicle
  9. metaphor and associated interfaces used in other samples of this series.
  10. FRESERVE introduces a new custom interface, IBall, and a new COM object,
  11. COBall. COBall implements the IBall interface. Both COBall and its
  12. in-process server are coded to support COM free threading in anticipation
  13. of their use by the free-threaded client, FRECLIEN, in the next lesson.
  14.  
  15. The CThreaded facility in APPUTIL is used to achieve thread safety as it
  16. was in the previous APTSERVE sample. COBall objects are derived from the
  17. CThreaded class and so inherit its OwnThis and UnOwnThis methods. These
  18. methods enforce mutually exclusive access to the FRESERVE server and to
  19. COBall objects managed by the server.
  20.  
  21. FRESERVE works with the FRECLIEN code sample to illustrate FRESERVE's COM
  22. server facilities in a free-threaded server and the subsequent manipulation
  23. of its components by a free-threaded client.
  24.  
  25. For functional descriptions and a tutorial code tour of FRESERVE, see the
  26. Code Tour section below. See also FRECLIEN.TXT in the sibling FRECLIEN
  27. directory for more details on the FRECLIEN client application and how it
  28. works with FRESERVE.DLL. You must build FRESERVE.DLL before building or
  29. running FRECLIEN.
  30.  
  31. FRESERVE's makefile automatically registers FRESERVE's DllBall component
  32. in the registry. This component must be registered before FRESERVE is
  33. available to outside COM clients as a server for that component. This
  34. self-registration is done using the REGISTER.EXE utility built in the
  35. previous REGISTER lesson. To build or run FRESERVE, you should build the
  36. REGISTER code sample first.
  37.  
  38. For details on setting up your system to build and test the code samples
  39. in this OLE Tutorial series, see TUTORIAL.TXT. The supplied MAKEFILE is
  40. Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE
  41. command in the Command Prompt window.
  42.  
  43. Usage
  44. -----
  45.  
  46. FRESERVE is a DLL that is intended primarily as a free-threaded COM
  47. server. Although it can be implicitly loaded by linking to its associated
  48. .LIB file, it is normally used after an explicit LoadLibrary call, usually
  49. from within COM's CoGetClassObject function. FRESERVE is a
  50. self-registering in-process server. The makefile that builds this sample
  51. automatically registers this server in the registry. You can manually
  52. initiate its self registration by issuing the following command at the
  53. command prompt:
  54.  
  55.   nmake register
  56.  
  57. This registration process requires a prior build of the REGISTER sample
  58. in this series.
  59.  
  60. To use FRESERVE, a client program does not need to include FRESERVE.H or
  61. link to FRESERVE.LIB. A COM client of FRESERVE obtains access solely
  62. through its component's CLSID and OLE services. For FRESERVE, that CLSID
  63. is CLSID_DllBall (defined in file BALLGUID.H in the INC sibling
  64. directory). The FRECLIEN code sample shows how the client obtains this
  65. access.
  66.  
  67.  
  68. CODE TOUR
  69. =========
  70.  
  71. Files         Description
  72.  
  73. FRESERVE.TXT  This file.
  74. MAKEFILE      The generic makefile for building the FRESERVE.DLL
  75.               code sample of this lesson.
  76. FRESERVE.H    The include file for declaring as imported or defining as
  77.               exported the service functions in FRESERVE.DLL.
  78. FRESERVE.CPP  The main implementation file for FRESERVE.DLL. Has DllMain
  79.               and the COM server functions (for example, DllGetClassObject).
  80. FRESERVE.RC   The DLL resource definition file for the executable.
  81. FRESERVE.ICO  The icon resource for the executable.
  82. SERVER.H      The include file for the server control C++ object.
  83. SERVER.CPP    The implementation file for the server control object.
  84. FACTORY.H     The include file for the server's class factory COM objects.
  85. FACTORY.CPP   The implementation file for the server's class factories.
  86. BALL.H        The include file for the COBall COM object class.
  87. BALL.CPP      The implementation file for the COBall COM object class.
  88.  
  89.  
  90. FRESERVE uses many of the utility classes and services provided by
  91. APPUTIL. For more details on APPUTIL, study the APPUTIL library's source
  92. code and APPUTIL.TXT, located in the sibling \APPUTIL directory.
  93.  
  94. This sample is part of a graduated series of tutorial samples. This tour
  95. assumes that you have some exposure to those previous samples. It does not
  96. revisit earlier topics of basic interface implementation techniques, COM
  97. object construction, in-process server construction, and class factory
  98. construction. For information on these topics, study the earlier tutorial
  99. samples.
  100.  
  101. The major topics covered in this code tour are an overview of how FRESERVE
  102. works; thread-safe mechanisms in FRESERVE; an overview of COM free
  103. threading; self-registration of COM objects in a free-threaded server; the
  104. IBall interface; the construction of the COBall COM object; and issues in
  105. class factory construction in a free-threaded server.
  106.  
  107. The COBall COM object is the single object type managed by this FRESERVE
  108. in-process server. COBall is constructed as an aggregatable COM object
  109. with a native implementation of the IBall interface. COBall exposes the
  110. IBall interface to allow clients to perform a small set of operations on
  111. an instance of COBall. The COBall object encapsulates data that defines a
  112. moving ball. Data such as position, size, and color are encapsulated. No
  113. graphical images are managed in this object. When a COBall object is
  114. initialized with a call to its Reset method, it is given a rectangle (a
  115. standard Win32 RECT structure) that defines the boundaries within which
  116. the ball may move. The COBall object contains coded logic to move the ball
  117. within those boundaries, to bounce the ball off any boundary when
  118. appropriate, and to provide clients with current data on the ball's
  119. location, size, and color. The object maintains a current color property
  120. indicating the particular executing thread that most recently moved the
  121. ball by calling the ball's Move method.
  122.  
  123. A client can then use the IBall interface to move the ball and to obtain
  124. the data necessary to paint an image of the moving ball. The COBall object
  125. is coded to support the free-threaded COM model, in which any number of
  126. threads may freely make asynchronous calls directly to the object's
  127. interface methods. In the following FRECLIEN code sample, several threads
  128. running concurrently attempt to move a single COBall object. An
  129. independent client thread runs concurrently to continuously obtain ball
  130. position and color data and to paint snapshot images that graphically
  131. reflect the COBall's changing data. Many different threads may
  132. concurrently attempt to change and read the ball's data. The ball is kept
  133. "alive" by one set of client threads, while another client thread
  134. passively paints images of the moving ball. Because the ball updates its
  135. own data to reflect the executing threads, the ball image painted by the
  136. client changes color as it moves to continuously reflect which thread is
  137. currently executing.
  138.  
  139. Aside from the COBall logic needed to maintain the bouncing ball as a
  140. mathematical entity, FRESERVE also requires some special code to support
  141. free threading. We will start with this code, because it borrows from
  142. techniques previously shown in the APTSERVE sample. Like APTSERVE, the
  143. FRESERVE server housing is constructed to guard shared data in the server
  144. from conflicting access by multiple concurrent threads of execution. The
  145. technique used to enforce serialized access to this server data is based
  146. on the use of Win32 mutexes. APPUTIL's CThreaded utility base class
  147. encapsulates the mutex protection mechanism. This utility is presented in
  148. detail in the APTSERVE lesson.
  149.  
  150. In the FRESERVE server housing, the CServer C++ class is derived from the
  151. CThreaded base class to inherit the OwnThis and UnOwnThis methods. These
  152. methods are used in bracketed pairs to protect access to CServer's data,
  153. such as the server's object and lock counts. One new addition to this
  154. in-process server housing is the CServer::CanUnloadNow method. It uses the
  155. CThreaded facility. Here it is from SERVER.CPP.
  156.  
  157.   HRESULT CServer::CanUnloadNow(void)
  158.   {
  159.     HRESULT hr = S_FALSE;
  160.     LONG cObjects, cLocks;
  161.  
  162.     if (OwnThis())
  163.     {
  164.       cObjects = m_cObjects;
  165.       cLocks = m_cLocks;
  166.  
  167.       hr = (0L==cObjects && 0L==cLocks) ? S_OK : S_FALSE;
  168.  
  169.       UnOwnThis();
  170.   }
  171.  
  172.     return hr;
  173.   }
  174.  
  175. The OwnThis, UnOwnThis pair is used to protect access to the server's
  176. m_CObjects and m_cLocks variables. Within the range of this protection,
  177. copies of the variable values are made on the local stack, and the copies
  178. are then used for most logic. The logic implemented here is to support the
  179. in-process server's familiar DllCanUnloadNow exported function. This
  180. arrangement is convenient because DllCanUnloadNow is not a method of a
  181. class that is derived from CThreaded, such that it can benefit from the
  182. OwnThis protection mechanism. Yet such protection is needed, because the
  183. server data is vulnerable to concurrent access by multithreaded clients.
  184. In fact, the server data is potentially vulnerable to concurrent access by
  185. multiple threads that could be spawned within the server or its
  186. components. The CanUnloadNow method benefits from CThreaded not only for
  187. the protection it offers, but also for the simple programming
  188. encapsulation it provides. Such encapsulation will pay off later if the
  189. free-threaded server evolves to one that spawns multiple threads within it.
  190.  
  191. The default model for multithreaded programming with COM is the apartment
  192. model. This model was presented in the previous APTSERVE and APTCLIEN
  193. samples, where multiple apartment threads were provided within a server.
  194. COM's support of the apartment model ensures that calls to interface
  195. methods on objects created within an apartment will always be on the same
  196. thread as that of the class factory that created the object. The first
  197. point of recognition by COM in this regard is the occasion of the object's
  198. first marshaled interface when the object is created. This is usually the
  199. IClassFactory interface pointer requested in the CoGetClassObject call. At
  200. this point of recognition COM associates the object with its apartment
  201. thread.
  202.  
  203. The apartment model supported by COM is convenient and largely transparent
  204. to the client. It enforces a serialized access among contending threads to
  205. a COM object. The object is always called on the thread that "owns" it,
  206. even if the caller is on a different thread. This model entails some
  207. overhead as COM performs thread switching during such cross-thread calls.
  208.  
  209. Performance can be significantly improved by using the free-threaded
  210. model. In this model, COM does not make thread switches on behalf of
  211. cross-thread calls to interface methods. Instead, COM freely ushers the
  212. call through on the same thread originating the call. This means that the
  213. thread-safe serialized access to the object that was enforced by the
  214. apartment model is not enforced by the free threading model. COM objects
  215. must therefore provide their own serialization for access by multiple
  216. threads. Earlier in this lesson we saw the CThreaded mechanism that
  217. provides such access safety in this sample.
  218.  
  219. The way that COM is informed that this in-process server and its managed
  220. components support the free-threaded model is by an additional entry in
  221. the component's registration in the registry. As with all the previous
  222. in-process servers of this series, the FRESERVE server self-registers the
  223. components it houses. Here is the DllRegisterServer function from
  224. FRESERVE.CPP.
  225.  
  226.   STDAPI DllRegisterServer(void)
  227.   {
  228.     HRESULT  hr = NOERROR;
  229.     TCHAR    szID[GUID_SIZE+1];
  230.     TCHAR    szCLSID[GUID_SIZE+1];
  231.     TCHAR    szModulePath[MAX_PATH];
  232.  
  233.     // Obtain the path to this module's executable file for later use.
  234.     GetModuleFileName(
  235.       g_pServer->m_hDllInst,
  236.       szModulePath,
  237.       sizeof(szModulePath)/sizeof(TCHAR));
  238.  
  239.     /*--------------------------------------------------------------------
  240.       Create registry entries for the DllBall Component.
  241.     --------------------------------------------------------------------*/
  242.     // Create some base key strings.
  243.     StringFromGUID2(CLSID_DllBall, szID, GUID_SIZE);
  244.     lstrcpy(szCLSID, TEXT("CLSID\\"));
  245.     lstrcat(szCLSID, szID);
  246.  
  247.     // Create ProgID keys.
  248.     SetRegKeyValue(
  249.       TEXT("DllBall1.0"),
  250.       NULL,
  251.       TEXT("DllBall Component - FRESERVE Code Sample"));
  252.     SetRegKeyValue(
  253.       TEXT("DllBall1.0"),
  254.       TEXT("CLSID"),
  255.       szID);
  256.  
  257.     // Create VersionIndependentProgID keys.
  258.     SetRegKeyValue(
  259.       TEXT("DllBall"),
  260.       NULL,
  261.       TEXT("DllBall Component - FRESERVE Code Sample"));
  262.     SetRegKeyValue(
  263.       TEXT("DllBall"),
  264.       TEXT("CurVer"),
  265.       TEXT("DllBall1.0"));
  266.     SetRegKeyValue(
  267.       TEXT("DllBall"),
  268.       TEXT("CLSID"),
  269.       szID);
  270.  
  271.     // Create entries under CLSID.
  272.     SetRegKeyValue(
  273.       szCLSID,
  274.       NULL,
  275.       TEXT("DllBall Component - FRESERVE Code Sample"));
  276.     SetRegKeyValue(
  277.       szCLSID,
  278.       TEXT("ProgID"),
  279.       TEXT("DllBall1.0"));
  280.     SetRegKeyValue(
  281.       szCLSID,
  282.       TEXT("VersionIndependentProgID"),
  283.       TEXT("DllBall"));
  284.     SetRegKeyValue(
  285.       szCLSID,
  286.       TEXT("NotInsertable"),
  287.       NULL);
  288.     SetRegKeyValue(
  289.       szCLSID,
  290.       TEXT("InprocServer32"),
  291.       szModulePath);
  292.     AddRegNamedValue(
  293.       szCLSID,
  294.       TEXT("InprocServer32"),
  295.       TEXT("ThreadingModel"),
  296.       TEXT("Free"));
  297.  
  298.     return hr;
  299.   }
  300.  
  301. The important new code here is the call to AddRegNamedValue. A new named
  302. value must be added to the InprocServer32 key. This value is not a subkey
  303. of InprocServer32. Registry keys can have a series of named values
  304. associated with them. The definition of the AddRegNamedValue is
  305. straightforward and is defined in FRESERVE.CPP. The named value added for
  306. the DllBall component is "ThreadingModel=Free". Since our component and
  307. server are fully thread-safe and are coded to support the free-threaded
  308. model, we specify "Free". This means that clients of this server must be
  309. initialized with COM as free-threaded client processes. We will see how
  310. this is done the next lesson, FRECLIEN.
  311.  
  312. Other string values for ThreadingModel are "Both" and "Apartment".
  313. "Apartment" means that the client process must be initialized with COM as
  314. apartment-threaded before COM will employ the server on the client's
  315. behalf. "Both" means that client processes can be initialized with COM as
  316. either free-threaded or apartment-threaded. COM will employ the server on
  317. their behalf in either case. If the ThreadingModel value is missing from
  318. the InprocServer32 key, the apartment model is the default.
  319.  
  320. The COBall COM objects manufactured by this server implement the IBall
  321. custom interface. Here is the IBall interface declaration from IBALL.H,
  322. located in the INC sibling directory.
  323.  
  324.   DECLARE_INTERFACE_(IBall, IUnknown)
  325.   {
  326.     // IUnknown methods.
  327.     STDMETHOD(QueryInterface) (THIS_ REFIID, PPVOID) PURE;
  328.     STDMETHOD_(ULONG,AddRef)  (THIS) PURE;
  329.     STDMETHOD_(ULONG,Release) (THIS) PURE;
  330.  
  331.     // IBall methods.
  332.     STDMETHOD(Reset)      (THIS_ RECT*, SHORT) PURE;
  333.     STDMETHOD(GetBall)    (THIS_ POINT*, POINT*, COLORREF*) PURE;
  334.     STDMETHOD_(BOOL,Move) (THIS_ BOOL) PURE;
  335.   };
  336.  
  337. Here are the matching implementation declarations within the COBall object
  338. class. See the CImpIBall nested class declaration in BALL.H.
  339.  
  340.      ...
  341.      ...
  342.      STDMETHODIMP   Reset(RECT* pNewRect, short nBallSize);
  343.      STDMETHODIMP   GetBall(POINT* pOrg, POINT* pExt, COLORREF* pcrColor);
  344.      STDMETHODIMP_(BOOL) Move(BOOL bAlive);
  345.      ...
  346.      ...
  347.  
  348. The Reset method accepts a RECT structure, which specifies the initial
  349. boundaries of the rectangle within which the ball entity is permitted to
  350. move. This rectangle typically matches the client area of a client
  351. application's window. Reset also accepts an initial ball diameter,
  352. specified in pixels.
  353.  
  354. The GetBall method is the way the client obtains current properties of the
  355. COBall object: its diameter, its color, and the location of its origin.
  356.  
  357. The Move method simply directs the COBall to move itself. The semantics of
  358. this method are left up to the COBall object. In the current sample,
  359. internal calculation logic is employed to give some continuity to the
  360. ball's motion. The ball moves in the current direction until it hits a
  361. boundary, at which point it bounces. This bounce is a reflection of its
  362. angle of incidence with the boundary. However, the angle of reflection
  363. does not always equal the angle of incidence. A small random skew factor
  364. is used both for the reflection angle and for the speed of the motion.
  365. This skew factor makes the ball's movement appear more natural.
  366.  
  367. When the bAlive parameter is set to FALSE, the Move method destroys the
  368. ball. In this sample, multiple threads give life to the ball. Because one
  369. thread could issue this termination command before another could know
  370. about it, Move also returns a Boolean value to notify any other threads
  371. that call Move if the ball is dead or alive. A return value of FALSE
  372. indicates that the ball is dead. A dead ball no longer moves. Any thread
  373. can move the ball, and any thread can kill the ball.
  374.  
  375. The construction of the COBall COM object is based on techniques seen in
  376. earlier samples of this series. The familiar technique of nested class
  377. declarations are used for the multiple interface implementations. Here is
  378. the COBall class declaration in BALL.H.
  379.  
  380.   class COBall : public IUnknown, public CThreaded
  381.   {
  382.     public:
  383.       // Main Object Constructor & Destructor.
  384.       COBall(IUnknown* pUnkOuter, CServer* pServer);
  385.       ~COBall(void);
  386.  
  387.       // IUnknown methods. Main object, non-delegating.
  388.       STDMETHODIMP         QueryInterface(REFIID, PPVOID);
  389.       STDMETHODIMP_(ULONG) AddRef(void);
  390.       STDMETHODIMP_(ULONG) Release(void);
  391.  
  392.     private:
  393.       // We declare nested class interface implementations here.
  394.  
  395.       class CImpIBall : public IBall, public CThreaded
  396.       {
  397.         public:
  398.           // Interface Implementation Constructor & Destructor.
  399.           CImpIBall(COBall* pBackObj, IUnknown* pUnkOuter);
  400.           ~CImpIBall(void);
  401.  
  402.           // IUnknown methods.
  403.           STDMETHODIMP         QueryInterface(REFIID, PPVOID);
  404.           STDMETHODIMP_(ULONG) AddRef(void);
  405.           STDMETHODIMP_(ULONG) Release(void);
  406.  
  407.           // IBall methods.
  408.           STDMETHODIMP   Reset(RECT* pNewRect, short nBallSize);
  409.           STDMETHODIMP   GetBall(POINT* pOrg, POINT* pExt, COLORREF* pcrColor);
  410.           STDMETHODIMP_(BOOL) Move(BOOL bAlive);
  411.  
  412.         private:
  413.           // Data private to this COBall interface implementation of IBall.
  414.           COBall*      m_pBackObj;     // Parent Object back pointer.
  415.           IUnknown*    m_pUnkOuter;    // Outer unknown for Delegation.
  416.  
  417.           // The following private data and methods constitute the working
  418.           // heart of COBall as an actual application object.
  419.           BOOL         m_bAlive;
  420.           RECT         m_WinRect;
  421.           int          m_nWidth;
  422.           int          m_nHeight;
  423.           int          m_xDirection;
  424.           int          m_yDirection;
  425.           BOOL         m_bNewPosition;
  426.           int          m_xPosition;
  427.           int          m_yPosition;
  428.           short        m_xSkew;
  429.           short        m_ySkew;
  430.           COLORREF     m_crColor;
  431.           CXForm       m_XForm;
  432.           CBallThread  m_BallThreads[MAX_BALLTHREADS];
  433.  
  434.           // Private methods for internal use.
  435.           void GetDimensions(POINT*);
  436.           void SetDimensions(int,int);
  437.           void GetDirection(POINT*);
  438.           void SetDirection(int,int);
  439.           void GetPosition(POINT*);
  440.           void SetPosition(int,int);
  441.           void CheckBounce(void);
  442.           void FindThread(void);
  443.       };
  444.  
  445.       // Make the otherwise private and nested IBall interface
  446.       // implementation a friend to COM object instantiations of this
  447.       // COBall COM object class.
  448.       friend CImpIBall;
  449.  
  450.       // Private data of COBall COM objects.
  451.  
  452.       // Nested IBall implementation instantiation.  This IBall interface
  453.       // is instantiated inside this COBall object as a native interface.
  454.       CImpIBall        m_ImpIBall;
  455.  
  456.       // Main Object reference count.
  457.       ULONG            m_cRefs;
  458.  
  459.       // Outer unknown (aggregation & delegation).
  460.       IUnknown*        m_pUnkOuter;
  461.  
  462.       // Pointer to this component server's control object.
  463.       CServer*         m_pServer;
  464.   };
  465.  
  466. The heart of the COBall object is coded inside the CImpIBall nested
  467. interface implementation. The advantage of this design is that it doesn't
  468. burden the main COBall object class with the details of the ball's motion.
  469. It encapsulates the coding logic of the IBall interface entirely within
  470. the interface implementation. Another advantage of this is that, if you
  471. were evolving something like a CBall C++ class from legacy code into a COM
  472. COBall implementation, the transition from CBall to CImpIBall is somewhat
  473. less complicated than it would be from CBall to the outer COBall class,
  474. where the nested interface implementations tend to dominate attention. In
  475. this sample the issue is not as pronounced as a case where the outer
  476. COBall class might have many nested interface class declarations.
  477.  
  478. Notice that the outer COBall class and the nested CImpIBall class are both
  479. derived from CThreaded to inherit the OwnThis thread safety mechanism. The
  480. methods of both these classes need this mechanism to protect data
  481. encapsulated in their C++ objects.
  482.  
  483. The CImpIBall class implements many internal private methods, such as
  484. SetPostion and FindThread. Of all these CImpIBall methods, only the
  485. IUnknown and IBall interface methods are exposed as public in the C++
  486. sense. The IBall interface exposes only the public Reset, GetBall, and
  487. Move methods. Other of the private methods, such as SetPosition, could be
  488. promoted to the status of public members of some new IBall2 evolution of
  489. the IBall interface. Should such an evolution occur, the COM contract
  490. requires that the new interface adopt a name and a new interface
  491. identifier (IID). However, it must be derived from IBall to inherit and
  492. retain its prior semantics.
  493.  
  494. The data that defines the ball is declared in the private area of
  495. CImpIBall. The m_BallThreads array is maintained by the object to map
  496. color attributes to the threads that call the object's Move method. In
  497. conjunction with the FindThread method, program logic assigns colors to
  498. passing threads and reuses those colors when previous threads revisit the
  499. object. As new threads are added to the array, each is assigned a new
  500. color. In the current sample, a random selection of 64 such thread colors
  501. is accommodated using the compile-time macro MAX_BALL_THREADS.
  502.  
  503. The CXForm class is also declared in BALL.H. It is part of the inner
  504. algorithms that govern ball behavior and is not relevant to the threading
  505. model presented in this lesson.
  506.  
  507. The class factory for the DllBall component, CFBall, is declared in
  508. FACTORY.H and implemented in FACTORY.CPP. This code is borrowed from many
  509. previous samples in this series. Like COBall, CFBall is derived from
  510. IUnknown and CThreaded using multiple inheritance. CThreaded gives the
  511. class factory its thread safety using the OwnThis mechanism seen earlier.
  512. There is one special issue worth mentioning.
  513.  
  514. The CFBall::Release has an odd arrangement of the OwnThis, UnOwnThis pair.
  515. Here is the code from FACTORY.CPP.
  516.  
  517.   STDMETHODIMP_(ULONG) CFBall::Release(void)
  518.   {
  519.     ULONG cRefs;
  520.  
  521.     if (OwnThis())
  522.     {
  523.       cRefs = --m_cRefs;
  524.  
  525.       if (0 == cRefs)
  526.       {
  527.         // We've reached a zero reference count for this COM object.
  528.         // So we tell the server housing to decrement its global object
  529.         // count so that the server will be unloaded if appropriate.
  530.         if (NULL != m_pServer)
  531.           m_pServer->ObjectsDown();
  532.  
  533.         // We artificially bump the main reference count to prevent
  534.         // reentrancy via the main object destructor.  Not really needed
  535.         // in this CFBall but a good practice because we are aggregatable
  536.         // and may at some point in the future add something entertaining
  537.         // like some Releases to the CFBall destructor.
  538.         m_cRefs++;
  539.         UnOwnThis();
  540.         delete this;
  541.       }
  542.       else
  543.         UnOwnThis();
  544.     }
  545.  
  546.     return cRefs;
  547.   }
  548.  
  549. The extra call to UnOwnThis is needed because of the "delete this"
  550. statement. The UnOwnThis call must precede the delete statement so that
  551. the currently owning thread will relinquish ownership of this object
  552. before an attempt is made to destroy the entire object. The object must
  553. remain in existence as long as OwnThis is using the governing mutex, but
  554. no longer.
  555.