OBJECT.CPP

/* 
* OBJECT.CPP
*
* Connectable Object implementation that supports the sample
* interface IDuckEvents.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#include "connect.h"


/*
* CConnObject::CConnObject
* CConnObject::~CConnObject
*
* Constructor Parameters:
* None
*/

CConnObject::CConnObject(void)
{
UINT i;

m_cRef=0;

for (i=0; i < CCONNPOINTS; i++)
m_rgpConnPt[i]=NULL;

return;
}

CConnObject::~CConnObject(void)
{
UINT i;

for (i=0; i < CCONNPOINTS; i++)
{
if (NULL!=m_rgpConnPt[i])
{
if (NULL!=m_rgpConnPt[i])
delete m_rgpConnPt[i];
}
}

return;
}



/*
* CConnObject::Init
*
* Purpose:
* Instantiates the interface implementations for this object.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if initialization succeeds, FALSE otherwise.
*/

BOOL CConnObject::Init(void)
{
UINT i;

//Create our connection points
for (i=0; i < CCONNPOINTS; i++)
{
m_rgpConnPt[i]=new CConnectionPoint(this, IID_IDuckEvents);

if (NULL==m_rgpConnPt[i])
return FALSE;

m_rgpConnPt[i]->AddRef();
}

return TRUE;
}



/*
* CConnObject::QueryInterface
*
* Purpose:
* Manages the interfaces for this object which supports the
* IUnknown, ISampleOne, and ISampleTwo interfaces.
*
* Parameters:
* riid REFIID of the interface to return.
* ppv PPVOID in which to store the pointer.
*
* Return Value:
* HRESULT NOERROR on success, E_NOINTERFACE if the
* interface is not supported.
*/

STDMETHODIMP CConnObject::QueryInterface(REFIID riid, PPVOID ppv)
{
//Always NULL the out-parameters
*ppv=NULL;

if (IID_IUnknown==riid || IID_IConnectionPointContainer==riid)
*ppv=this;

if (NULL==*ppv)
return ResultFromScode(E_NOINTERFACE);

((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}



/*
* CConnObject::AddRef
* CConnObject::Release
*
* Reference counting members. When Release sees a zero count
* the object destroys itself.
*/

DWORD CConnObject::AddRef(void)
{
return ++m_cRef;
}

DWORD CConnObject::Release(void)
{
if (0!=--m_cRef)
return m_cRef;

delete this;
return 0;
}



/*
* CConnObject::EnumConnectionPoints
*
* Purpose:
* Creates and returns an enumerator object with the
* IEnumConnectionPoints interface that will enumerate the
* individual connection points supported in this object.
*
* Parameters:
* ppEnum LPENUMCONNECTIONPOINTS in which to store the
* IEnumConnectionPoints pointer.
*
* Return Value:
* HRESULT NOERROR on success, E_OUTOFMEMORY on failure or
* other error code.
*/

STDMETHODIMP CConnObject::EnumConnectionPoints
(LPENUMCONNECTIONPOINTS *ppEnum)
{
IConnectionPoint *rgCP[CCONNPOINTS];
UINT i;
PCEnumConnectionPoints pEnum;

*ppEnum=NULL;

for (i=0; i < CCONNPOINTS; i++)
rgCP[i]=(IConnectionPoint *)m_rgpConnPt[i];

//Create the enumerator: we only have one connection point
pEnum=new CEnumConnectionPoints(this, CCONNPOINTS, rgCP);

if (NULL==pEnum)
return ResultFromScode(E_OUTOFMEMORY);

pEnum->AddRef();
*ppEnum=pEnum;
return NOERROR;
}



/*
* CConnObject::FindConnectionPoint
*
* Purpose:
* Returns a pointer to the IConnectionPoint for a given
* outgoing IID.
*
* Parameters:
* riid REFIID of the outgoing interface for which
* a connection point is desired.
* ppCP IConnectionPoint ** in which to return
* the pointer after calling AddRef.
*
* Return Value:
* HRESULT NOERROR if the connection point is found,
* E_NOINTERFACE if it's not supported.
*/

STDMETHODIMP CConnObject::FindConnectionPoint(REFIID riid
, IConnectionPoint **ppCP)
{
*ppCP=NULL;

if (IID_IDuckEvents==riid)
{
return m_rgpConnPt[0]->QueryInterface(IID_IConnectionPoint
, (PPVOID)ppCP);
}

return ResultFromScode(E_NOINTERFACE);
}



/*
* CConnObject::TriggerEvent
*
* Purpose:
* Functions to make each connection point generate calls
* to any connected sinks. Since these functions are specific
* to IDuckEvents, they only deal with the connection point
* for that one interface
*
* Parameters:
* iEvent UINT of the event to trigger, either
* EVENT_QUACK, EVENT_FLAP, or EVENT_PADDLE.
*
* Return Value:
* BOOL TRUE events are triggered, FALSE if there
* are no connected sinks.
*/

BOOL CConnObject::TriggerEvent(UINT iEvent)
{
IEnumConnections *pEnum;
CONNECTDATA cd;

if (FAILED(m_rgpConnPt[0]->EnumConnections(&pEnum)))
return FALSE;

while (NOERROR==pEnum->Next(1, &cd, NULL))
{
IDuckEvents *pDuck;

if (SUCCEEDED(cd.pUnk->QueryInterface(IID_IDuckEvents
, (PPVOID)&pDuck)))
{
switch (iEvent)
{
case EVENT_QUACK:
pDuck->Quack();
break;

case EVENT_FLAP:
pDuck->Flap();
break;

case EVENT_PADDLE:
pDuck->Paddle();
break;
}

pDuck->Release();
}

cd.pUnk->Release();
}

pEnum->Release();
return TRUE;
}







//Connection Point Enumerator follows


/*
* CEnumConnectionPoints::CEnumConnectionPoints
* CEnumConnectionPoints::~CEnumConnectionPoints
*
* Parameters (Constructor):
* pUnkRef LPUNKNOWN to use for reference counting.
* cPoints ULONG number of connection points in prgpCP
* rgpCP IConnectionPoint** to the array to enumerate.
*/

CEnumConnectionPoints::CEnumConnectionPoints(LPUNKNOWN pUnkRef
, ULONG cPoints, IConnectionPoint **rgpCP)
{
UINT i;

m_cRef=0;
m_pUnkRef=pUnkRef;

m_iCur=0;
m_cPoints=cPoints;
m_rgpCP=new IConnectionPoint *[(UINT)cPoints];

if (NULL!=m_rgpCP)
{
for (i=0; i < cPoints; i++)
{
m_rgpCP[i]=rgpCP[i];
m_rgpCP[i]->AddRef();
}
}

return;
}


CEnumConnectionPoints::~CEnumConnectionPoints(void)
{
if (NULL!=m_rgpCP)
{
UINT i;

for (i=0; i < m_cPoints; i++)
m_rgpCP[i]->Release();

delete [] m_rgpCP;
}

return;
}




/*
* CEnumConnectionPoints::QueryInterface
* CEnumConnectionPoints::AddRef
* CEnumConnectionPoints::Release
*
* Purpose:
* IUnknown members for CEnumConnectionPoints object.
*/

STDMETHODIMP CEnumConnectionPoints::QueryInterface(REFIID riid
, LPVOID *ppv)
{
*ppv=NULL;

if (IID_IUnknown==riid || IID_IEnumConnectionPoints==riid)
*ppv=(LPVOID)this;

if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}

return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(ULONG) CEnumConnectionPoints::AddRef(void)
{
++m_cRef;
m_pUnkRef->AddRef();
return m_cRef;
}

STDMETHODIMP_(ULONG) CEnumConnectionPoints::Release(void)
{
m_pUnkRef->Release();

if (0L!=--m_cRef)
return m_cRef;

delete this;
return 0;
}





/*
* CEnumConnectionPoints::Next
*
* Purpose:
* Returns the next element in the enumeration.
*
* Parameters:
* cPoints ULONG number of connection points to return.
* ppCP IConnectionPoint** in which to store the returned
* pointers.
* pulEnum ULONG * in which to return how many we
* enumerated.
*
* Return Value:
* HRESULT NOERROR if successful, S_FALSE otherwise,
*/

STDMETHODIMP CEnumConnectionPoints::Next(ULONG cPoints
, IConnectionPoint **ppCP, ULONG *pulEnum)
{
ULONG cReturn=0L;

if (NULL==m_rgpCP)
return ResultFromScode(S_FALSE);

if (NULL==ppCP)
return ResultFromScode(E_POINTER);

if (NULL==pulEnum)
{
if (1L!=cPoints)
return ResultFromScode(E_POINTER);
}
else
*pulEnum=0L;

if (NULL==*ppCP || m_iCur >= m_cPoints)
return ResultFromScode(S_FALSE);

while (m_iCur < m_cPoints && cPoints > 0)
{
*ppCP=m_rgpCP[m_iCur++];

if (NULL!=*ppCP)
(*ppCP)->AddRef();

ppCP++;
cReturn++;
cPoints--;
}

if (NULL!=pulEnum)
*pulEnum=cReturn;

return NOERROR;
}







STDMETHODIMP CEnumConnectionPoints::Skip(ULONG cSkip)
{
if (((m_iCur+cSkip) >= m_cPoints) || NULL==m_rgpCP)
return ResultFromScode(S_FALSE);

m_iCur+=cSkip;
return NOERROR;
}


STDMETHODIMP CEnumConnectionPoints::Reset(void)
{
m_iCur=0;
return NOERROR;
}


STDMETHODIMP CEnumConnectionPoints::Clone
(LPENUMCONNECTIONPOINTS *ppEnum)
{
PCEnumConnectionPoints pNew;

*ppEnum=NULL;

//Create the clone
pNew=new CEnumConnectionPoints(m_pUnkRef, m_cPoints, m_rgpCP);

if (NULL==pNew)
return ResultFromScode(E_OUTOFMEMORY);

pNew->AddRef();
pNew->m_iCur=m_iCur;

*ppEnum=pNew;
return NOERROR;
}