#ifndef __AdviseHolder_h__
#define __AdviseHolder_h__
/////////////////////////////////////////////////////////////////////////////
// AdviseHolder.h | Declaration of the TCAdviseHolder class.
//
#pragma warning(disable: 4786)
#if !defined(_INC_COMDEF)
#include <comdef.h>
#endif // !defined(_INC_COMDEF)
#ifndef __RefCountedData_h__
#include "RefCountedData.h"
#endif // !__RefCountedData_h__
/////////////////////////////////////////////////////////////////////////////
// TCAdviseHolder provides an encapsulation of the semantics used to connect
// to and disconnect from a connectable component object. An object is
// connectable in this sense if it supports the IConnectionPointContainer
// interface, first introduced by Microsoft for use with ActiveX controls.
//
// The class simplifies the connecting to an object by providing the Advise
// method, which takes three parameters and performs the necessary sequence
// of interface method calls. In this process, the class saves the /cookie/
// returned from the connectable object, which identifies the connection when
// Unadvise is called to disconnect from the object.
//
// While the connection process can happen from the single method call, it
// can also be split into a two step process using the FindConnectionPoint
// method, followed by a single-parameter form of the Advise method. This
// allows for a more advanced usage of the class.
//
// The class is implemented using a reference counting scheme to avoid
// unnecessary connections and disconnections when passing the object by
// value. This makes it suitable for use within an STL container object, such
// as std::map or std::vector. Actually, that was the primary motivation of
// this class.
//
// When the Unadvise method is called, which happens automatically from the
// destructor, m_pData (a pointer to an XDataType instance) is released. Upon
// releasing the last reference, the XDataType instance will delete itself,
// which actually disconnects from the connectable object.
//
// See Also: TCAdviseHolder::XData, TCRefCountedData
class TCAdviseHolder
{
// Construction / Destruction
public:
TCAdviseHolder();
TCAdviseHolder(const TCAdviseHolder& that);
virtual ~TCAdviseHolder();
// Attributes
public:
IConnectionPointContainer* GetCPC();
IConnectionPoint* GetCP();
// Operations
public:
HRESULT FindConnectionPoint(IUnknown* punkCP, REFIID iid);
HRESULT Advise(IUnknown* punk);
HRESULT Advise(IUnknown* punkCP, IUnknown* punk, REFIID iid);
HRESULT Unadvise();
// Operators
public:
const TCAdviseHolder& operator=(const TCAdviseHolder& that);
// Group=Types
protected:
///////////////////////////////////////////////////////////////////////////
// Description: Nested class.
// Remarks: XData is a nested class of TCAdviseHolder which represents the
// reference counted data used by that class. There may be multiple
// instances of TCAdviseHolder that each point to the same instance of this
// class. Actually, XData is the base class of the XDataType, which adds
// the reference counting implementation to XData.
class XData
{
// Construction / Destruction
public:
XData();
virtual ~XData();
// Operations
public:
HRESULT Unadvise();
// Data Members:
public:
// Smart pointer that maintains the IConnectionPointContainer interface.
IConnectionPointContainerPtr m_pCPC;
// Smart pointer that maintains the IConnectionPoint interface.
IConnectionPointPtr m_pCP;
// Cookie provided by the connectable object to identify this connection.
DWORD m_dwAdvise;
};
///////////////////////////////////////////////////////////////////////////
// Description: Reference counted derivation of XData.
// See Also: TCRefCountedData, TCAdviseHolder::XData
typedef TCRefCountedData<XData> XDataType;
// Group=Data Members
protected:
XDataType* m_pData; // Pointer to the reference counted data.
};
/////////////////////////////////////////////////////////////////////////////
// Group=Construction / Destruction
/////////////////////////////////////////////////////////////////////////////
inline TCAdviseHolder::TCAdviseHolder()
: m_pData(NULL)
{
}
/////////////////////////////////////////////////////////////////////////////
// Parameters:
// that - Another instance to be copied into this one. The m_pData member
// is copied and TCRefCountedData::AddRef is called on the pointer.
//
// See Also: TCAdviseHolder::operator=, TCRefCountedData::AddRef
inline TCAdviseHolder::TCAdviseHolder(const TCAdviseHolder& that)
: m_pData(NULL)
{
operator=(that);
}
/////////////////////////////////////////////////////////////////////////////
// Remarks: The m_pData member is release by calling
// TCRefCountedData::Release.
//
// See Also: TCRefCountedData::Release
inline TCAdviseHolder::~TCAdviseHolder()
{
m_pData->Release();
}
/////////////////////////////////////////////////////////////////////////////
// Group=Attributes
/////////////////////////////////////////////////////////////////////////////
// Gets the IConnectionPointContainer interface pointer of the current
// connection, if any.
//
// Return Value: The IConnectionPointContainer interface pointer of the
// current connection, if any.
//
// See Also: TCAdviseHolder::GetCP, TCAdviseHolder::Advise,
// TCAdviseHolder::FindConnectionPoint
inline IConnectionPointContainer* TCAdviseHolder::GetCPC()
{
return m_pData ? m_pData->m_pCPC : NULL;
}
/////////////////////////////////////////////////////////////////////////////
// Gets the IConnectionPoint interface pointer of the current connection, if
// any.
//
// Return Value: The IConnectionPoint interface pointer of the current
// connection, if any.
//
// See Also: TCAdviseHolder::GetCPC, TCAdviseHolder::Advise
inline IConnectionPoint* TCAdviseHolder::GetCP()
{
return m_pData ? m_pData->m_pCP : NULL;
}
/////////////////////////////////////////////////////////////////////////////
// Group=Operations
/////////////////////////////////////////////////////////////////////////////
// Description: Finds the specified outgoing interface in an object.
//
// Parameters:
// punkCP - An interface pointer of an object for which the outgoing
// interface, /iid/, is to be found.
// iid - The outgoing interface GUID which represents the connection point
// to be found.
//
// Remarks: This method is used internally by the three-parameter Advise
// method. It is made public to enable a more advanced usage of the class. Do
// not use this method directly unless you have a need to find a connection
// point seperately from setting up the advisory connection.
//
// This method first queries /punkCP/ for the IConnectionPointContainer
// interface. If the object supports this interface, then the
// *IConnectionPointContainer::FindConnectionPoint* method is called to get
// the connection point for the interface specified by /iid/. The interface
// pointers for both the connection point and its container are stored.
// Follow this method call by calling the single-parameter Advise
// method to actually complete the advisory connection to the object.
//
// Return Value: This method supports the standard return values
// E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:
//
// S_OK - The object supports the specified connection point interface.
// E_NOINTERFACE - The object does not support connection points.
// CONNECT_E_NOCONNECTION - The object does not support the specified
// outgoing connection point interface.
//
// See Also: TCAdviseHolder::GetCP, TCAdviseHolder::GetCPC,
// TCAdviseHolder::Advise
inline
HRESULT TCAdviseHolder::FindConnectionPoint(IUnknown* punkCP, REFIID iid)
{
// Allocate a new reference counted data object
HRESULT hr = S_OK;
XDataType* pData = NULL;
__try
{
// Release any previous reference counted data object
m_pData->Release();
m_pData = NULL;
// Allocate a new reference counted data object
if (!(pData = new XDataType))
return E_OUTOFMEMORY;
// Query for the specified object's IConnectionPointContainer
pData->m_pCPC = punkCP;
if (NULL != pData->m_pCPC)
{
// Get the object's IID_IPropertyNotifySink connection point
hr = pData->m_pCPC->FindConnectionPoint(iid, &pData->m_pCP);
if (SUCCEEDED(hr))
{
// Save the new reference counted data object
m_pData = pData;
// Indicate success
return S_OK;
}
}
else
{
hr = E_NOINTERFACE;
}
}
__except(1)
{
hr = E_POINTER;
}
// Release the shared data object
pData->Release();
// Return the last HRESULT
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// Description: Sets up an advisory connection to an outgoing interface of a
// connectable object.
//
// Parameters:
// punk - An interface pointer that is to receive the advisory
// notifications (events) from the source object. This interface must
// support the outgoing interface through QueryInterface.
// punkCP - An interface pointer of an object for which the outgoing
// interface, /iid/, is to be found.
// iid - The outgoing interface GUID which represents the connection point
// to be found.
//
// Remarks: The three-parameter form of this method is the only method that
// needs to be called on a new instance of this class. This method will
// perform all the steps necessary to set up an advisory connection to an
// outgoing interface of a connectable object. This method is the recommended
// way of using this class for most cases.
//
// The single-parameter form of this method must be used in conjunction with
// the FindConnectionPoint method and is provided for a more advanced usage
// of the class.
//
// Return Value: This method supports the standard return values
// E_OUTOFMEMORY and E_UNEXPECTED, as well as the following:
//
// S_OK - The object supports the specified connection point interface.
// E_NOINTERFACE - The object does not support connection points.
// CONNECT_E_NOCONNECTION - The object does not support the specified
// outgoing connection point interface.
//
// See Also: TCAdviseHolder::GetCP, TCAdviseHolder::GetCPC,
// TCAdviseHolder::FindConnectionPoint, TCAdviseHolder::XData::m_pCPC,
// TCAdviseHolder::XData::m_pCP
inline HRESULT TCAdviseHolder::Advise(IUnknown* punk)
{
// Ensure that FindConnectionPoint has been called previously
if (!m_pData || NULL == m_pData->m_pCPC || NULL == m_pData->m_pCP ||
m_pData->m_dwAdvise)
{
_ASSERT(false);
return E_UNEXPECTED;
}
// Set the advise
HRESULT hr = m_pData->m_pCP->Advise(punk, &m_pData->m_dwAdvise);
if (FAILED(hr))
{
_TRACE1("TCAdviseHolder::Advise(): CP->Advise returneed 0x%08X\n", hr);
Unadvise();
}
// Return the last HRESULT
return hr;
}
inline
HRESULT TCAdviseHolder::Advise(IUnknown* punkCP, IUnknown* punk, REFIID iid)
{
HRESULT hr = FindConnectionPoint(punkCP, iid);
if (SUCCEEDED(hr))
hr = Advise(punk);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// Description: Terminates the advisory connection.
//
// Return Value: This method supports the standard return value E_UNEXPECTED,
// as well as the following:
//
// S_OK - The connection was successfully terminated.
// CONNECT_E_NOCONNECTION - No advisory connection exists.
//
// Remarks: Terminates the advisory connection previously established through
// Advise.
//
// See Also: TCAdviseHolder::Advise, TCAdviseHolder::XData::Unadvise
inline HRESULT TCAdviseHolder::Unadvise()
{
return m_pData ? m_pData->Unadvise() : CONNECT_E_NOCONNECTION;
}
/////////////////////////////////////////////////////////////////////////////
// Group=Operators
/////////////////////////////////////////////////////////////////////////////
// Parameters:
// that - Another instance to be copied into this one. The m_pData member
// is copied and TCRefCountedData::AddRef is called on the pointer.
//
// TODO: This method /should/ call TCRefCountedData::Release on the current
// m_pData, if not NULL, prior to assigning it the new pointer value.
//
// See Also: TCAdviseHolder::constructor, TCRefCountedData::AddRef
inline
const TCAdviseHolder& TCAdviseHolder::operator=(const TCAdviseHolder& that)
{
m_pData = that.m_pData;
m_pData->AddRef();
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// TCAdviseHolder::XData
/////////////////////////////////////////////////////////////////////////////
// Group=Construction / Destruction
inline TCAdviseHolder::XData::XData()
: m_dwAdvise(0)
{
}
inline TCAdviseHolder::XData::~XData()
{
Unadvise();
}
/////////////////////////////////////////////////////////////////////////////
// Group=Operations
/////////////////////////////////////////////////////////////////////////////
// Description: Terminates the advisory connection.
//
// Return Value: This method supports the standard return value E_UNEXPECTED,
// as well as the following:
//
// S_OK - The connection was successfully terminated.
// CONNECT_E_NOCONNECTION - No advisory connection exists.
//
// Remarks: Terminates the advisory connection previously established through
// Advise.
//
// See Also: TCAdviseHolder::m_pCPC, TCAdviseHolder::m_pCP,
// TCAdviseHolder::Unadvise
inline HRESULT TCAdviseHolder::XData::Unadvise()
{
// Release any IConnectionPointContainer
if (m_pCPC)
m_pCPC = NULL;
// Disconnect the advisory connection, if any
HRESULT hr = CONNECT_E_NOCONNECTION;
if (m_dwAdvise)
{
hr = m_pCP->Unadvise(m_dwAdvise);
m_dwAdvise = 0;
}
// Release any IConnectionPoint
if (m_pCP)
m_pCP = NULL;
// Return the last HRESULT
return hr;
}
/////////////////////////////////////////////////////////////////////////////
#endif // !__AdviseHolder_h__