CONSERVE - Connectable COM objects in an in-process server


SUMMARY
=======

The CONSERVE sample shows how to construct a connectable COM object,
COBall, in a thread-safe in-process server. To expose connectable object
features, COBall implements the standard COM interface,
IConnectionPointContainer. COBall also implements the custom interface,
IBall, that was introduced in the FRESERVE sample.

The principal focus of CONSERVE is the connectable object support in
COBall and how connected clients are notified of ball events. CONSERVE
works with the CONCLIEN client code sample.

CONSERVE's COBall implements a virtual moving ball similar to the one
coded in the FRESERVE sample. However, rather than FRESERVE's
free-threaded COM object, CONSERVE houses COBall in an apartment model
server. COBall defines the internal logic of a moving ball that exists
within a specified two-dimensional bounding rectangle. COBall provides
only a description of a ball. It is up to the client to create a graphical
representation based on the data managed in COBall.

Clients move the virtual ball using its IBall interface. COBall's internal
logic will bounce the ball when it collides with a boundary. The client
can obtain the ball's current position, size, and color to permit display
of the ball's moving image. The client can also use the connection
facilities of COBall to receive notifications of ball bounce events. The
CONCLIEN sample will use these connection facilities to produce various
sounds when the ball bounces.

CONSERVE uses the CThreaded facility in APPUTIL for thread safety, as in
the FRESERVE sample. COBall objects are derived from the CThreaded class
and inherit its OwnThis and UnOwnThis methods. These methods allow only
one thread at a time to have access to the CONSERVE server and to COBall
objects managed by the server.

For functional descriptions and a tutorial code tour of CONSERVE, see the
Code Tour section below. See also CONCLIEN.TXT in the sibling CONCLIEN
directory for more details on the CONCLIEN client application and how it
works with CONSERVE.DLL. You must build CONSERVE.DLL before building or
running CONCLIEN.

CONSERVE's makefile automatically registers the DllSndBall component in
the registry. This component must be registered before CONSERVE is
available to outside clients as a server for that component. This
self-registration is done using the REGISTER.EXE utility built in the
REGISTER lesson. To build or run CONSERVE, you should build the REGISTER
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
-----

CONSERVE is a DLL that is intended primarily as an in-process server.
Although it can be implicitly loaded by linking to its associated .LIB
file, it is normally used after an explicit LoadLibrary call, usually from
within COM's CoGetClassObject function. CONSERVE is a self-registering
in-process server. The makefile that builds this sample automatically
registers this server in the registry. You can manually initiate its
self-registration by issuing the following command at the command prompt:

  nmake register

This registration process requires a prior build of the REGISTER sample
in this series.

To use CONSERVE, a client program does not need to include CONSERVE.H or
link to CONSERVE.LIB. A client of CONSERVE obtains access solely through
its object's CLSID and OLE services. For CONSERVE, that CLSID is
CLSID_DllSndBall (defined in BALLGUID.H in the \INC sibling directory).
The CONCLIEN code sample shows how the client obtains this access.


CODE TOUR
=========

Files         Description

CONSERVE.TXT  This file.
MAKEFILE      The generic makefile for building the CONSERVE.DLL
              code sample of this lesson.
CONSERVE.H    The include file for declaring as imported or defining as
              exported the service functions in CONSERVE.DLL.
CONSERVE.CPP  The main implementation file for CONSERVE.DLL. Has DllMain
              and the COM server functions (for example, DllGetClassObject).
CONSERVE.RC   The DLL resource definition file for the executable.
CONSERVE.ICO  The icon resource for the executable.
SERVER.H      The include file for the server control C++ object.
SERVER.CPP    The implementation file for the server control object.
FACTORY.H     The include file for the server's class factory COM objects.
FACTORY.CPP   The implementation file for the server's class factories.
CONNECT.H     The include file for the connection point enumerator,
              connection point, and connection enumerator classes.
CONNECT.CPP   The implementation file for the connection point enumerator,
              connection point, and connection enumerators objects.
BALL.H        The include file for the COBall object class.
BALL.CPP      The implementation file for the COBall object class
              and the connection points.

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

This sample is part of a graduated series of tutorial samples. This tour
assumes that you have some exposure to those previous samples. It does not
revisit earlier topics of basic interface implementation techniques, COM
object construction, in-process server construction, and class factory
construction. For information on these topics, study the earlier tutorial
samples.

The major topics covered in this code tour are:

  An overview of how CONSERVE connectability works
  Connectable object mechanisms in COBall
  The connection point container in COBall
  The connection point object
  Event notification
  The connection enumerator object
  The connection point enumerator object

The COBall object is the single object type managed by this CONSERVE
in-process server. COBall is constructed as a connectable COM object with
an implementation of the standard IConnectionPointContainer interface and
an implementation of the custom IBall interface.

COBall exposes the IBall interface so clients can perform a small set of
ball-related operations on an instance of COBall. Methods are exposed to
reset, move, and obtain current ball position, size, and color. This IBall
interface is the same interface used in the FRESERVE sample. See
FRESERVE.TXT for more details on the IBall interface.

The COBall object encapsulates data, such as position, size, and color,
that defines a moving ball. No graphical images are managed in COBall.
When a COBall object is initialized with a call to its Reset method, it is
given a rectangle (a Win32 RECT structure) that defines the boundaries
within which the ball may move. The COBall object contains coded logic to
move the ball within those boundaries, to bounce the ball off any boundary
when appropriate, and to provide clients with current data on the ball's
location, size, and color. A client can use the IBall interface to move
the ball and to obtain the data necessary to paint an image of the moving
ball.

COBall exposes the IConnectionPointContainer interface so clients can
connect to COBall to receive notifications of specified events that occur
in COBall. By exposing this interface, COBall becomes a connectable
object. A client can request this interface with a call to QueryInterface
and use it to obtain the object's connection points. The client
participation in this scheme is covered in the associated CONCLIEN sample.

Basically, the client implements what is called a sink object with a sink
interface. The sink interface receives event notifications from COBall
after the sink is properly connected to a COBall instance. The client
makes the connection by using a connection point object that is managed by
COBall. There can be numerous connection points on a connectable object.
In the CONSERVE sample, COBall has only one connection point, which
reports ball bounce events.

A connection point can support numerous client connections. The connection
point in COBall maintains an array of connections that can grow
dynamically at run time. We will see later in this tour how COBall
maintains and uses this array to broadcast event notifications.

To make COBall fully connectable, several COM object classes must be
implemented: COEnumConnectionPoints, COConnectionPoint, and
COEnumConnections. The implementation of the standard
IConnectionPointContainer interface within COBall is also necessary. The
COEnumConnectionPoints object exposes the standard IEnumConnectionPoints
interface so clients can enumerate or sequence through the connection
points of the connectable COM object. The COConnectionPoint object exposes
the standard IConnectionPoint interface so that clients can connect to the
connectable COM object using the connection point. The COEnumConnections
object exposes the standard IEnumConnections interface so clients can
enumerate through the current connections managed by a connection point.
In this CONSERVE sample, the bulk of the code for these implementations is
in files CONNECT.H and CONNECT.CPP. The rest of this tour will focus on
these implementations.

Enumeration objects are common in OLE programming. The basic scheme relies
on a dynamically created enumeration object that exposes an interface used
for subsequent access to an enumerated sequence of other objects that are
managed by the object being enumerated. For example, the COBall
implementation of the IConnectionPointContainer interface has an
EnumConnectionPoints method. This method allows clients to enumerate the
connection points of the COBall object. It dynamically creates a special
object, COEnumConnectionPoints, whose sole purpose is to enable access by
the client through a standard IEnumConnectionPoint interface to an
enumerated sequence of the connection points. This interface is
implemented by the programmer in a COEnumConnectionPoints object. It has
Next, Skip, Reset, and Clone methods which clients can use to sequence
through the enumerated connection points. In this sample, connection
points themselves support enumeration of the connections they manage. A
COEnumConnections object can be dynamically created. On the same
programming model as IEnumConnectionPoints, COEnumConnections exposes an
IEnumConnections interface with Next, Skip, Reset, and Clone methods.

The COBall implementation of IConnectionPointContainer must provide the
FindConnectionPoint and EnumConnectionPoints methods. Once a client
obtains an IConnectionPointContainer interface on COBall, it can call
FindConnectionPoint to obtain a specific connection point, or it can call
EnumConnectionPoints to enumerate all connection points. Once the client
obtains a connection point, it can connect to it (Advise method),
disconnect from it (Unadvise method), enumerate all current connections to
that connection point (EnumConnections method), obtain the container of
the connection point (GetConnectionPointContainer method), and obtain the
outgoing interface of the client notification sink (GetConnectionInterface
method). When the client connects to a COBall connection point, it passes
a pointer to the sink interface on its sink object. The connection point
object maintains a dynamic list of such sink interfaces. When events occur
within COBall that require sending a notification to the sink in the
client, logic in COBall can use each connection point's sink interface
list to broadcast appropriate notifications.

In this connection scheme, the client implements sink objects for the
connection points it expects to find in COBall. On the server side, COBall
must implement the IConnectionPointContainer interface. This interface
implementation, in turn, requires certain other COM objects. Each
connection point requires a COConnectionPoint object. To enumerate
connection points in COBall requires a COEnumConnectionPoints object, and
to enumerate the connections of a connection point requires a
COEnumConnections object. We will cover enumerator objects later in this
code tour.

COBall's implementation of the IConnectionPointContainer interface is
coded in BALL.H and BALL.CPP. The construction of the COBall object is
based on techniques presented in earlier samples of this series. Nested
class declarations are used for the multiple interface implementations.
Here is the COBall class declaration in BALL.H.

  class COBall : public IUnknown, public CThreaded
  {
    public:
      // Main Object Constructor & Destructor.
      COBall(IUnknown* pUnkOuter, CServer* pServer);
      ~COBall(void);

      // A general method for initializing this newly created object.
      // Creates any subordinate arrays, structures, or objects.
      HRESULT Init(void);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

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

      class CImpIConnectionPointContainer : public IConnectionPointContainer,
                                            public CThreaded
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIConnectionPointContainer(
            COBall* pBackObj,
            IUnknown* pUnkOuter);
          ~CImpIConnectionPointContainer(void);

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

          // IConnectionPointContainer methods.
          STDMETHODIMP         FindConnectionPoint(
                                 REFIID,
                                 IConnectionPoint**);
          STDMETHODIMP         EnumConnectionPoints(IEnumConnectionPoints**);

        private:
          // Data private to this interface implementation.
          COBall*       m_pBackObj;     // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.
      };

      class CImpIBall : public IBall, public CThreaded
      {
        public:
          // Interface Implementation Constructor & Destructor.
          CImpIBall(COBall* pBackObj, IUnknown* pUnkOuter);
          ~CImpIBall(void);

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

          // IBall methods.
          STDMETHODIMP         Reset(RECT* pNewRect, short nBallSize);
          STDMETHODIMP         GetBall(
                                 POINT* pNewOrg,
                                 POINT* pNewExt,
                                 COLORREF* pcrColor);
          STDMETHODIMP_(BOOL)  Move(BOOL bAlive);

        private:
          // Data private to this interface implementation of IBall.
          COBall*       m_pBackObj;     // Parent Object back pointer.
          IUnknown*     m_pUnkOuter;    // Outer unknown for Delegation.

          // The following private data and methods constitute the working
          // heart of COBall as an actual application object.
          BOOL          m_bAlive;
          RECT          m_WinRect;
          int           m_nWidth;
          int           m_nHeight;
          int           m_xDirection;
          int           m_yDirection;
          BOOL          m_bNewPosition;
          int           m_xPosition;
          int           m_yPosition;
          short         m_xSkew;
          short         m_ySkew;
          COLORREF      m_crColor;
          CXForm        m_XForm;
          CBallThread   m_aBallThreads[MAX_BALLTHREADS];

          // Private utility methods for internal support of the custom
          // IBall semantics.
          void GetDimensions(POINT*);
          void SetDimensions(int,int);
          void GetDirection(POINT*);
          void SetDirection(int,int);
          void GetPosition(POINT*);
          void SetPosition(int,int);
          void FindThread(void);
          DWORD CheckBounce(void);
      };

      // Make the otherwise private and nested IBall and
      // IConnectionPointContainer interface implementations a friend to
      // COM object instantiations of this COBall COM object class.
      friend CImpIConnectionPointContainer;
      friend CImpIBall;

      // Private method of main connectable COBall COM object to broadcast
      // event notifications to all connected listening sinks.
      HRESULT NotifySinks(DWORD dwEvent);

      // Private data of COBall COM objects.

      // Nested IBall implementation instantiation.  This IBall interface
      // is instantiated inside this COBall object as a native interface.
      CImpIBall         m_ImpIBall;

      // Nested IConnectionPointContainer implementation instantiation.
      CImpIConnectionPointContainer m_ImpIConnectionPointContainer;

      // Main Object reference count.
      ULONG             m_cRefs;

      // Outer unknown (aggregation & delegation).
      IUnknown*         m_pUnkOuter;

      // Pointer to this component server's control object.
      CServer*          m_pServer;

      // The array of connection points for this connectable COM object.
      IConnectionPoint* m_aConnectionPoints[MAX_CONNECTION_POINTS];
  };

The outer COBall class and the nested CImpIBall and
CImpIConnectionPointContainer classes are derived from CThreaded to
inherit the OwnThis thread safety mechanism. The methods of these classes
use this mechanism to ensure mutually exclusive access to their objects by
multiple threads. Though COBall is not registered as a free-threaded
component in this sample, it is coded for thread-safety and could
therefore easily be used in a free-threaded program.

Much of this COBall class declaration was presented in the FRESERVE
sample. The items related to connectable object support are the
CImpIConnectionPointContainer implementation, the COBall::NotifySinks
private method, and the COBall::m_aConnectionPoints array.

NotifySinks is an internal convenience method that consolidates the
broadcast to client sinks of all appropriate events occurring in COBall.
We will look at NotifySinks in detail later.

The m_aConnectionPoints array is COBall's main array of connection points.
In this sample it is declared with a compile-time constant,
MAX_CONNECTION_POINTS. This means that the connection points supported by
COBall are determined at compile time. The m_aConnectionPoints array is
initially assigned during COBall::Init in file BALL.CPP.

  HRESULT COBall::Init(void)
  {
    HRESULT hr = NOERROR;
    COConnectionPoint* pCOConnPt;

    // Rig this COBall COM object to be connectable. Assign the connection
    // point array. This object's connection points are determined at
    // compile time--it currently has only one connection point:
    // the CONNPOINT_BALLSINK connection point. Create a connection
    // point object for this and assign it into the array. This array could
    // easily grow to support additional connection points in the future.

    // First try creating a new connection point object. Pass 'this' as the
    // pHostObj pointer used by the connection point to pass its AddRef and
    // Release calls back to the host connectable object.
    pCOConnPt = new COConnectionPoint(this);
    if (NULL != pCOConnPt)
    {
      // If creation succeeded then initialize it (including creating
      // its initial dynamic connection array).
      hr = pCOConnPt->Init(IID_IBallSink);

      // If the init succeeded then use QueryInterface to obtain the
      // IConnectionPoint interface on the new connection point object.
      // The interface pointer is assigned directly into the
      // connection point array. The QI also does the needed AddRef.
      if (SUCCEEDED(hr))
        hr = pCOConnPt->QueryInterface(
                          IID_IConnectionPoint,
                          (PPVOID)&m_aConnectionPoints[CONNPOINT_BALLSINK]);
    }
    else
      hr = E_OUTOFMEMORY;

    return hr;
  }

The call to COBall::Init is done in COBall's class factory in the
CreateInstance method, which is called when a client asks COM to create an
instance of a DllSndBall object. The class factory for CONSERVE's
DllSndBall object, CFBall, is declared in FACTORY.H and implemented in
FACTORY.CPP. This factory code is adapted from many previous samples in
this series. Like COBall, CFBall is derived from IUnknown and CThreaded
using multiple inheritance. CThreaded gives the class factory its thread
safety using the OwnThis mechanism seen earlier.

This COBall::Init method creates the connection point object, initializes
it, and uses a QueryInterface call on the connection point to assign the
new connection point to COBall's connection point array. The
CONNPOINT_BALLSINK connection point is the only connection point currently
supported by COBall objects. To add other connection points would require
additional code in Init for each one. CONNPOINT_BALLSINK is a constant
defined in CONNECT.H. Similar constants would be needed for additional
connection points.

The COConnectionPoint object class is declared in CONNECT.H.

  class COConnectionPoint : public IConnectionPoint, public CThreaded
  {
    public:
      // Main Object Constructor & Destructor.
      COConnectionPoint(IUnknown* pHostObj);
      ~COConnectionPoint(void);

      // A general method for initializing this newly created object.
      // Creates any subordinate arrays, structures, or objects.
      HRESULT Init(REFIID riid);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // IConnectionPoint methods.
      STDMETHODIMP GetConnectionInterface(IID*);
      STDMETHODIMP GetConnectionPointContainer(IConnectionPointContainer**);
      STDMETHODIMP Advise(IUnknown*, DWORD*);
      STDMETHODIMP Unadvise(DWORD);
      STDMETHODIMP EnumConnections(IEnumConnections**);

    private:
      // Private utility methods of COConnectionPoint.
      HRESULT GetSlot(UINT* puiFreeSlot);
      HRESULT FindSlot(DWORD dwCookie, UINT* puiSlot);

      // Private data of COConnectionPoint COM objects.

      // Main Object reference count.
      ULONG          m_cRefs;

      // IUnknown pointer to host COM object offering this connection point.
      IUnknown*      m_pHostObj;

      // The IID of the sink interface associated with this connection point.
      IID            m_iidSink;

      // The current connection cookie (key) counter.
      DWORD          m_dwNextCookie;

      // The current number of live sink connections to this connection point.
      UINT           m_cConnections;

      // The current maximum index into the dynamic connection array.
      UINT           m_uiMaxIndex;

      // The dynamic array of sink connections to this connection point.
      CONNECTDATA*   m_paConnections;
  };

This code includes many of the fixtures that are common to COM objects
housed in an in-process server. For example, the member m_cRefs is the
object reference counter. IConnectionPoint is not implemented in
COConnectionPoint in the familiar pattern of nested class implementation.
Instead, COConnectionPoint is derived directly from IConnectionPoint. This
direct derivation can be done as a convenience because COConnectionPoint
objects are not aggregatable and are not exposed as public components
housed in this server. COConnectionPoint objects are managed as internal
objects within the server.

The m_pHostObj pointer is passed into the constructor for use later in the
connection point's GetConnectionPointContainer method.

The m_iidSink member variable stores a reference to the IID of the sink
interface in the client. This member is solely for the use of the
GetConnectionInterface method.

COConnectionPoint objects manage a dynamic list of current connections to
the connection point using the m_dwNextCookie, m_cConnections,
m_uiMaxIndex, and m_paConnections member variables. The m_paConnections
member points to the current dynamic array of connections. Each connection
in the array is represented by an OLE-declared CONNECTDATA structure. The
structure contains the following.

  typedef struct tagCONNECTDATA
  {
    IUnknown* pUnk;
    DWORD     dwCookie;
  } CONNECTDATA;

The pUnk member contains the interface pointer for the sink in the client.
The dwCookie member contains a unique key used to designate the connection.

The initial dynamic array of these elements is created by
COConnectionPoint::Init. Here is Init from CONNECT.CPP.

  HRESULT COConnectionPoint::Init(
            REFIID riid)
  {
    HRESULT hr = NOERROR;
    CONNECTDATA* paConns;

    // Keep a copy of the reference to the IID of the sink interface
    // associated with this connection point. Needed for potential
    // use by the GetConnectionInterface method.
    m_iidSink = riid;

    // Build the initial dynamic array for connections.
    paConns = new CONNECTDATA[ALLOC_CONNECTIONS];
    if (NULL != paConns)
    {
      // Zero the array.
      memset(paConns, 0, ALLOC_CONNECTIONS * sizeof(CONNECTDATA));

      // Rig this connection point object so that it will use the
      // new internal array of connections.
      m_uiMaxIndex = ALLOC_CONNECTIONS;
      m_paConnections = paConns;
    }
    else
      hr = E_OUTOFMEMORY;

    return (hr);
  }

This implementation of Init creates an initial allocated array of empty
connection elements and assigns member m_paConnections as a pointer to
that array. The content of these array elements is assigned when
COConnectionPoint::Advise is called, and the content is cleared by
COConnectionPoint::Unadvise. Here is Advise from CONNECT.CPP.

  STDMETHODIMP COConnectionPoint::Advise(
                 IUnknown* pUnkSink,
                 DWORD* pdwCookie)
  {
    HRESULT hr = NOERROR;
    UINT uiFreeSlot = 0;
    IUnknown* pISink = NULL;

    if (OwnThis())
    {
      // Zero the output connection key.
      *pdwCookie = 0;

      // Get the specific associated client Sink interface that this
      // connection point expects to use for notifications.
      hr = pUnkSink->QueryInterface(m_iidSink, (PPVOID)&pISink);
      if (SUCCEEDED(hr))
      {
        // Store the specific sink interface in this connection point's
        // array of live connections. First find a free slot (expand the
        // array if needed).
        hr = GetSlot(&uiFreeSlot);
        if (SUCCEEDED(hr))
        {
          // Assign the new slot with the connection entry.
          m_paConnections[uiFreeSlot].pUnk = pISink;
          m_paConnections[uiFreeSlot].dwCookie = m_dwNextCookie;

          // Assign the output Cookie value.
          *pdwCookie = m_dwNextCookie;

          // Increment the Cookie counter.
          m_dwNextCookie++;

          // Increment the number of live connections.
          m_cConnections++;
        }
      }
      else
        hr = CONNECT_E_CANNOTCONNECT;

      UnOwnThis();
    }

    return hr;
  }

Advise is called by a client to establish a connection of its sink
interface to the connection point of a connectable object. The client
passes a pointer to the IUnknown of its sink object and a pointer to a
"cookie" variable. Advise assigns a unique key value to this client cookie
variable and adds the new connection to the connection array created by
Init. The connection point maintains m_dwNextCookie as a counter to
provide such cookies. When a connection is properly assigned, Advise also
increments a counter of current live connections, m_cConnections. Because
these counters might be accessed by multiple client threads, the entire
COConnectionPoint class is derived from CThreaded to permit the Advise and
Unadvise methods to use the OwnThis mechanism to ensure thread-safety. For
details on the CThreaded class, see APTSERVE.TXT in the APTSERVE code
sample of this series.

Advise calls the GetSlot private method to obtain an index into the
connection array of an open slot. Here is GetSlot from CONNECT.CPP.

  HRESULT COConnectionPoint::GetSlot(
            UINT* puiFreeSlot)
  {
    HRESULT hr = NOERROR;
    BOOL bOpen = FALSE;
    UINT i;
    CONNECTDATA* paConns;

    // Zero the output variable.
    *puiFreeSlot = 0;

    // Loop to find an empty slot.
    for (i=0; i<m_uiMaxIndex; i++)
    {
      if (m_paConnections[i].dwCookie == 0)
      {
        // We found an open empty slot.
        *puiFreeSlot = i;
        bOpen = TRUE;
        break;
      }
    }

    if (!bOpen)
    {
      // We didn't find an existing open slot in the array--it's full.
      // Expand the array by ALLOC_CONNECTIONS entries and assign the
      // appropriate output index.
      paConns = new CONNECTDATA[m_uiMaxIndex + ALLOC_CONNECTIONS];
      if (NULL != paConns)
      {
        // Copy the content of the old full array to the new larger array.
        for (i=0; i<m_uiMaxIndex; i++)
        {
          paConns[i].pUnk = m_paConnections[i].pUnk;
          paConns[i].dwCookie = m_paConnections[i].dwCookie;
        }

        // Zero (ie mark as empty) the expanded portion of the new array.
        for (i=m_uiMaxIndex; i<m_uiMaxIndex+ALLOC_CONNECTIONS; i++)
        {
          paConns[i].pUnk = NULL;
          paConns[i].dwCookie = 0;
        }

        // New larger array is ready--delete the old array.
        delete [] m_paConnections;

        // Rig the connection point to use the new larger array.
        m_paConnections = paConns;

        // Assign the output free slot as first entry in new expanded area.
        *puiFreeSlot = m_uiMaxIndex;

        // Calculate the new max index.
        m_uiMaxIndex += ALLOC_CONNECTIONS;
      }
      else
        hr = E_OUTOFMEMORY;
    }

    return hr;
  }

GetSlot first attempts to find an open slot in the existing array and will
return it if one is found. Such an open slot exists because a previous
client call to Unadvise cleared the entry. However, if the current array
is full and GetSlot can find no existing open slot, GetSlot will allocate
an entirely new larger array with room for ALLOC_CONNECTIONS more entries.
ALLOC_CONNECTIONS is a constant declared in CONNECT.H. The m_uiMaxIndex
member is used to maintain the current maximum index of the array. The
m_uiMaxIndex member stores the size of the array so any for loops that
iterate through the array always work, no matter how the size of the array
has changed in dynamic reallocations of the array.

The client calls Unadvise to disconnect its sink interface from the
connection point. Here is Unadvise from CONNECT.CPP.

  STDMETHODIMP COConnectionPoint::Unadvise(
                 DWORD dwCookie)
  {
    HRESULT hr = NOERROR;
    UINT uiSlot;

    if (0 != dwCookie)
    {
      if (OwnThis())
      {
        hr = FindSlot(dwCookie, &uiSlot);
        if (SUCCEEDED(hr))
        {
          // Release the sink interface.
          RELEASE_INTERFACE(m_paConnections[uiSlot].pUnk);

          // Mark the array entry as empty.
          m_paConnections[uiSlot].dwCookie = 0;

          // Decrement the number of live connections.
          m_cConnections--;
        }

        UnOwnThis();
      }
    }
    else
      hr = E_INVALIDARG;

    return hr;
  }

Unadvise releases the sink interface of the specified connection and sets
the associated pointer to NULL, zeroes the associated cookie value, and
decrements the number of live connections. Unadvise is passed a cookie
value that was obtained by a previous call to Advise. This cookie value is
used to locate the connection in the connection array. The FindSlot
private method is called to find the array index to the connection entry
corresponding to the specified cookie value. Here is FindSlot from
CONNECT.CPP.

  HRESULT COConnectionPoint::FindSlot(
            DWORD dwCookie,
            UINT* puiSlot)
  {
    HRESULT hr = CONNECT_E_NOCONNECTION;
    UINT i;

    // Loop to find the Cookie.
    for (i=0; i<m_uiMaxIndex; i++)
    {
      if (dwCookie == m_paConnections[i].dwCookie)
      {
        // If a cookie match is found, assign the output slot index.
        *puiSlot = i;
        hr = NOERROR;
        break;
      }
    }

    return hr;
  }

When a match of the specified cookie is found, the *puiSlot index is
assigned.

With an array of client sink connections established by client calls to
Advise, COBall can use the array to broadcast notifications to the client
sinks. We return to BALL.CPP to study notifications. Here is COBall::Move.

  STDMETHODIMP_(BOOL) COBall::CImpIBall::Move(
                        BOOL bAlive)
  {
    BOOL bResult = FALSE;
    DWORD dwEvent;

    if (OwnThis())
    {
      if (bAlive)
      {
        // Find thread that is now executing this code. Remember its Id and
        // assign it a color. If this thread previously visited here then
        // use its remembered values. In any case, set a color value in
        // m_crColor of its existing or newly assigned color.
        FindThread();

        // Ask the Ball if it has hit any of the edges of the current window
        // rectangle. If so, it will recalculate its position and direction
        // to achieve a "bounce" effect in its motion the next time it is
        // painted. CheckBounce also determines and returns any notification
        // events.
        dwEvent = CheckBounce();

        // Send notification of each bounce event to any listening sinks.
        m_pBackObj->NotifySinks(dwEvent);

        // Calculate and set new Ball position.
        if(m_bNewPosition)
        {
          m_bNewPosition = FALSE;
          m_XForm.Clear();
          m_XForm.Trans(m_xPosition, m_yPosition);
        }
        else
          m_XForm.Trans(m_xDirection, m_yDirection);
      }
      else
        m_bAlive = FALSE;

      bResult = m_bAlive;

      UnOwnThis();
    }

    return bResult;
  }

This implementation is very similar to that of the Move method presented
in the COBall object of the FRESERVE code sample. In the CONSERVE sample,
when the CONCLIEN client calls IBall::Move on this COBall object, COBall
has internal logic in the CheckBounce private method to determine if
certain events have occurred. CheckBounce returns a constant indicating
the event. Move uses this event constant to call the NotifySinks private
method. Here is NotifySinks from BALL.CPP.

  HRESULT COBall::NotifySinks(
         DWORD dwEvent)
  {
    HRESULT hr = NOERROR;
    IConnectionPoint* pIConnectionPoint;
    IEnumConnections* pIEnum;
    CONNECTDATA ConnData;

    // If there was a bounce event, broadcast appropriate notifications to
    // all Sinks connected to each connection point.
    if (BOUNCE_NONE != dwEvent)
    {
      // Here is the section for the BallSink connection point--currently
      // this is the only connection point offered by COBall objects.
      pIConnectionPoint = m_aConnectionPoints[CONNPOINT_BALLSINK];
      if (NULL != pIConnectionPoint)
      {
        pIConnectionPoint->AddRef();
        hr = pIConnectionPoint->EnumConnections(&pIEnum);
        if (SUCCEEDED(hr))
        {
          // Loop thru the connection point's connections and if the
          // listening connection supports IBallSink (ie, BallSink events)
          // then dispatch the dwEvent event notification to that sink.
          while (NOERROR == pIEnum->Next(1, &ConnData, NULL))
          {
            IBallSink* pIBallSink;

            hr = ConnData.pUnk->QueryInterface(
                                  IID_IBallSink,
                                  (PPVOID)&pIBallSink);
            if (SUCCEEDED(hr))
            {
              switch (dwEvent)
              {
                case BOUNCE_BOTTOM:
                  pIBallSink->BounceBottom();
                  break;
                case BOUNCE_LEFT:
                  pIBallSink->BounceLeft();
                  break;
                case BOUNCE_RIGHT:
                  pIBallSink->BounceRight();
                  break;
                case BOUNCE_TOP:
                  pIBallSink->BounceTop();
                  break;
                default:
                  break;
              }
              pIBallSink->Release();
            }
            ConnData.pUnk->Release();
          }
          pIEnum->Release();
        }
        pIConnectionPoint->Release();
      }
    }

    return hr;
  }

Since this COBall object has one connection point determined at compile
time, NotifySinks can index into the connection point array for specific
connection points. In this case, only one connection point is known, and
its index is CONNPOINT_BALLSINK.

Once the connection point is obtained, a connection enumerator is used.
The connection point's EnumConnections method is called to obtain an
IEnumConnections interface pointer on a COEnumConnections object. A while
loop iterates through the connections with calls to pIEnum->Next. Once the
data for each connection is obtained, the client's sink interface is used
to make specific notification calls. In this case, the IBallSink interface
has methods to handle the BOUNCE_BOTTOM, BOUNCE_LEFT, BOUNCE_RIGHT, and
BOUNCE_BOTTOM events. The custom client sink interface was designed to
match the expected connection points in the connectable objects.

This enumeration scheme is a common pattern in OLE programming. Here is
the COConnectionPoint::EnumConnections method from CONNECT.CPP.

  STDMETHODIMP COConnectionPoint::EnumConnections(
                 IEnumConnections** ppIEnum)
  {
    HRESULT hr = OLE_E_NOCONNECTION;
    CONNECTDATA* paConns;
    COEnumConnections* pCOEnum;
    UINT i,j;

    if (OwnThis())
    {
      // Zero the output enumerator interface pointer.
      *ppIEnum = NULL;

      if (0 != m_cConnections)
      {
        // Create an array of CONNECTDATA structures.
        paConns = new CONNECTDATA[(UINT)m_cConnections];
        if (NULL != paConns)
        {
          for (i=0, j=0; i<m_uiMaxIndex && j<m_cConnections; i++)
          {
            // Copy non-empty entries only.
            if (0 != m_paConnections[i].dwCookie)
            {
              // Assign the occupied entry.
              paConns[j].pUnk = (IUnknown*)m_paConnections[i].pUnk;
              paConns[j].dwCookie = m_paConnections[i].dwCookie;
              j++;
            }
          }

          // Create a new COM object for enumerating connections. Pass
          // 'this' as a pHostObj pointer used later to ensure the host
          // connection point object stays alive as long as the enumerator
          // that enumerates connections to that connection point.
          pCOEnum = new COEnumConnections(this);
          if (NULL != pCOEnum)
          {
            // Use the previously constructed (paConns) array of connections
            // to init the new COEnumConnections COM object. The Init will
            // build yet another internal copy of this array. Set the
            // initial enumerator index to 0.
            hr = pCOEnum->Init(m_cConnections, paConns, 0);

            // QueryInterface to return the requested interface pointer.
            // An AddRef will be conveniently done by the QI.
            if (SUCCEEDED(hr))
              hr = pCOEnum->QueryInterface(
                              IID_IEnumConnections,
                              (PPVOID)ppIEnum);
          }
          else
            hr = E_OUTOFMEMORY;

          // We're done with the locally constructed array copy--delete it.
          delete [] paConns;
        }
        else
          hr = E_OUTOFMEMORY;
      }

      UnOwnThis();
    }

    return hr;
  }

A special copy of the connection array is constructed containing only the
active connections. Other empty entries are ignored. This array copy is
used in the initialization of a COEnumConnections COM object. Once created
and initialized, this object is queried for its IEnumConnections
interface. A pointer to this interface is passed to the caller of
COConnectionPoint::EnumConnections.

To enumerate the connections of a connection point, a COEnumConnections
object must be created. Here is the declaration of COEnumConnections from
CONNECT.H.

  class COEnumConnections : public IEnumConnections
  {
    public:
      // Main Object Constructor & Destructor.
      COEnumConnections(IUnknown* pHostObj);
      ~COEnumConnections(void);

      // A general method for initializing this newly created object.
      // Creates any subordinate arrays, structures, or objects.
      HRESULT Init(
                ULONG cConnections,
                CONNECTDATA* paConnections,
                ULONG iEnumIndex);

      // IUnknown methods. Main object, non-delegating.
      STDMETHODIMP         QueryInterface(REFIID, PPVOID);
      STDMETHODIMP_(ULONG) AddRef(void);
      STDMETHODIMP_(ULONG) Release(void);

      // IEnumConnections methods.
      STDMETHODIMP         Next(ULONG, CONNECTDATA*, ULONG*);
      STDMETHODIMP         Skip(ULONG);
      STDMETHODIMP         Reset(void);
      STDMETHODIMP         Clone(IEnumConnections**);

    private:
      // Private data of COEnumConnections COM objects.

      // Main Object reference count.
      ULONG            m_cRefs;

      // IUnknown pointer to host connection point COM object being
      // enumerated.
      IUnknown*        m_pHostObj;

      // Connection index variable.
      ULONG            m_iEnumIndex;

      // Number of Connections being enumerated.
      ULONG            m_cConnections;

      // Allocated array of live Connections only.
      CONNECTDATA*     m_paConnections;
  };

To ensure that the host connection point object remains in existence as
long as the COEnumConnections enumerator object, the m_pHostObj member is
used to delegate AddRef and Release calls. The value for m_pHostObj is
initially passed into COEnumConnections in its constructor. To support the
IEnumConnections interface, a current connection array index is maintained
in member m_iEnumIndex. The total number of connections in the array is
also retained in member m_cConnections. An internal copy of the connection
array is also kept by COEnumConnections. The m_paConnections member points
to this array.

The COEnumConnections::Init method assigns m_pHostObj, m_cConnections, and
m_iEnumIndex. The Init method also allocates and assigns an internal copy
of the connection array and assigns m_paConnections to point to it.

Enumerator objects offer the Next, Skip, Reset, and Clone methods of the
standard IEnumConnections interface. The IEnumConnections interface is
implemented directly by COEnumConnections rather than being coded as a
nested interface implementation. This is convenient for such ad hoc COM
objects as COEnumConnections, which are subordinate to the other objects
housed in the server and which are not components in their own right.

The COEnumConnections::Next method is called to return a specified number
of next connections from the connection array. Here is Next from
CONNECT.CPP.

  STDMETHODIMP COEnumConnections::Next(
                 ULONG cReq,
                 CONNECTDATA* paConnections,
                 ULONG* pcEnumerated)
  {
    HRESULT hr = NOERROR;
    ULONG cRet = 0;

    // Make sure the argument values passed are valid.
    if (NULL != m_paConnections)
    {
      if (NULL != paConnections)
      {
        if (m_iEnumIndex < m_cConnections)
        {
          if (NULL != pcEnumerated)
            *pcEnumerated = 0L;
          else
            if (1L != cReq)
              hr = E_POINTER;
        }
        else
          hr = S_FALSE;
      }
      else
        hr = E_POINTER;
    }
    else
      hr = S_FALSE;

    if (SUCCEEDED(hr))
    {
      // Starting at the current Enumerator index, loop to assign the
      // requested number of output connection data structures.
      for (; m_iEnumIndex < m_cConnections && cReq > 0;
             paConnections++, m_iEnumIndex++, cRet++, cReq--)
      {
        // Because we are assigning a copy of a connection's data, AddRef
        // its sink interface pointer.
        if (NULL != m_paConnections[m_iEnumIndex].pUnk)
          m_paConnections[m_iEnumIndex].pUnk->AddRef();

        // Assign a connection's data from the inside Enumerator array to
        // the specified output receiving array.
        *paConnections = m_paConnections[m_iEnumIndex];
      }

      // Assign the output number of connections enumerated.
      if (NULL != pcEnumerated)
        *pcEnumerated = cRet;
    }

    return hr;
  }

Argument cReq is the number of connection items requested. Argument
paConnections is a pointer to a caller-allocated array of the CONNECTDATA
items requested. The caller must ensure that the array size is large
enough to accept the number of requested items. Argument pcEnumerated is a
pointer to a variable that will contain, upon return, the number of items
actually returned. Because copies of the connection data are being passed
back to the caller, AddRef must be called on the sink interface pointer in
each CONNECTDATA element.

The Skip method is called to start at the current enumeration index and
skip a specified number of connection entries. The Reset method is called
to reset the current enumeration index to 0. The Clone method is called to
create a new copy of the entire COEnumConnections object, with internal
connection array and index the same as the original.

Earlier in this tour we saw that COBall's connection points could also be
enumerated by a call to COBall's implementation of the
IConnectionPointContainer interface. IConnectionPointContainer has an
EnumConnectionPoints method that can be called by clients that want to
enumerate all the connection points of the connectable object. Unlike the
EnumConnections presented earlier, EnumConnectionPoints is not called by
any object within the CONSERVE server. The
IConnectionPointContainer::EnumConnectionPoints method relies on the
creation of a COEnumConnectionPoints COM object and works very similar to
the COEnumConnections just presented. It implements the
IEnumConnectionPoints interface with methods Next, Skip, Reset, and Clone.
You can study the source for COEnumConnectionPoints in files CONNECT.H and
CONNECT.CPP.
