/* Copyright (c) 1998-1999 Microsoft Corporation Module Name: blbcoen.h Abstract: Author: */ #ifndef __BLB_COLLECTION_ENUMERATION_IMPL__ #define __BLB_COLLECTION_ENUMERATION_IMPL__ #include "resource.h" #include #include "blberr.h" #include "blbgen.h" #include "sdp.h" // forward declaration class CSdpConferenceBlob; template class ENUM_ELEMENT { public: inline ENUM_ELEMENT(); inline void SuccessInit( IN T &Element, IN BOOL DestroyElementOnDestruction = FALSE ); inline T &GetElement(); inline T &GetContent(); inline void SetDestroyElementFlag(); virtual ~ENUM_ELEMENT(); protected: T *m_Element; BOOL m_DestroyElementOnDestruction; }; template inline ENUM_ELEMENT::ENUM_ELEMENT( ) : m_Element(NULL), m_DestroyElementOnDestruction(FALSE) { } template inline void ENUM_ELEMENT::SuccessInit( IN T &Element, IN BOOL DestroyElementOnDestruction /* = FALSE */ ) { ASSERT(NULL == m_Element); m_Element = ∈ m_DestroyElementOnDestruction = DestroyElementOnDestruction; } template inline T & ENUM_ELEMENT::GetElement( ) { ASSERT(NULL != m_Element); return *m_Element; } template inline void ENUM_ELEMENT::SetDestroyElementFlag( ) { ASSERT(NULL != m_Element); m_DestroyElementOnDestruction = TRUE; } template inline T & ENUM_ELEMENT::GetContent( ) { ASSERT(NULL != m_Element); return *m_Element; } template /* virtual */ ENUM_ELEMENT::~ENUM_ELEMENT( ) { if ( m_DestroyElementOnDestruction ) { ASSERT(NULL != m_Element); delete m_Element; } } template class IF_ARRAY : public CArray { protected: typedef typename T::SDP_LIST SDP_LIST; typedef typename T::ELEM_IF ELEM_IF; typedef CArray BASE; public: inline IF_ARRAY(); HRESULT Init( IN CSdpConferenceBlob &ConfBlob, IN SDP_LIST &SdpList ); inline ELEM_IF *GetAt( IN UINT Index ); HRESULT Add( IN UINT Index, IN ELEM_IF *ElemIf ); inline void Delete( IN UINT Index ); inline UINT GetSize(); inline SDP_LIST *GetSdpList(); inline VARIANT *GetData(); inline ELEM_IF **GetElemIfArrayData(); inline CSdpConferenceBlob *GetSdpBlob(); inline void ClearSdpBlobRefs(); ~IF_ARRAY(); protected: CSdpConferenceBlob *m_ConfBlob; SDP_LIST *m_SdpList; CArray m_ElemIfArray; }; template inline IF_ARRAY::IF_ARRAY( ) : m_ConfBlob(NULL), m_SdpList(NULL) { } template HRESULT IF_ARRAY::Init( IN CSdpConferenceBlob &ConfBlob, IN SDP_LIST &SdpList ) { ASSERT(NULL == m_ConfBlob); ASSERT(NULL == m_SdpList); // create the array in 3 steps - // i) create each of the instances and insert into the list // ii) set the sdp list destroy members flag to FALSE // iii) set the destroy element flag to TRUE for each of the created instances // this order is needed to ensure that only one of (sdp list, T instance) is responsible // for deleting the sdp instance // for each sdp specific data structure, create and initialize a COM component, // set the corresponding element in the interface array to the queried interface ptr for (UINT i=0; (int)i < SdpList.GetSize(); i++) { // create an instance of the component supporting the elem if CComObject *CompInstance; HRESULT HResult = CComObject::CreateInstance(&CompInstance); BAIL_ON_FAILURE(HResult); // initialize the instance with the sdp specific data structure CompInstance->SuccessInit(ConfBlob, *((T::SDP_TYPE *)SdpList.GetAt(i))); // query for the elem interface T::ELEM_IF *ElemIf; // query for the element interface and return it HResult = CompInstance->_InternalQueryInterface(T::ELEM_IF_ID, (void**)&ElemIf); if ( FAILED(HResult) ) { delete CompInstance; return HResult; } // initialize the variant wrapper VARIANT ElemVariant; V_VT(&ElemVariant) = VT_DISPATCH; V_DISPATCH(&ElemVariant) = ElemIf; // the ElemIf is stored twice (although it was incremented once in _InternalQueryInterface // need to keep this in mind when releasing the interfaces INT_PTR Index; try { Index = m_ElemIfArray.Add(ElemIf); } catch(...) { delete CompInstance; return E_OUTOFMEMORY; } try { BASE::Add(ElemVariant); } catch(...) { m_ElemIfArray.RemoveAt(Index); delete CompInstance; return E_OUTOFMEMORY; } } // inform the sdp list that there is no need to destroy the members on destruction SdpList.ClearDestroyMembersFlag(); // for each of the inserted instances, set the destroy element flag to true for (i=0; (int)i < BASE::GetSize(); i++) { ((T *)m_ElemIfArray.GetAt(i))->SetDestroyElementFlag(); } m_ConfBlob = &ConfBlob; m_SdpList = &SdpList; return S_OK; } template inline typename IF_ARRAY::ELEM_IF * IF_ARRAY::GetAt( IN UINT Index ) { ASSERT(Index < (UINT)BASE::GetSize()); return m_ElemIfArray.GetAt(Index); } template HRESULT IF_ARRAY::Add( IN UINT Index, IN ELEM_IF *ElemIf ) { ASSERT(NULL != m_SdpList); ASSERT(BASE::GetSize() == m_SdpList->GetSize()); ASSERT(Index <= (UINT)BASE::GetSize()); ASSERT(NULL != ElemIf); // shift elements with equal or higher indices forwards // cheat COM here, and get the sdp specific class instance for the elem if // initialize the variant wrapper VARIANT ElemVariant; V_VT(&ElemVariant) = VT_DISPATCH; V_DISPATCH(&ElemVariant) = ElemIf; // insert into the arrays try { m_ElemIfArray.InsertAt(Index, ElemIf); } catch(...) { return E_OUTOFMEMORY; } try { BASE::InsertAt(Index, ElemVariant); } catch(...) { m_ElemIfArray.RemoveAt(Index); return E_OUTOFMEMORY; } try { m_SdpList->InsertAt(Index, &(((T *)ElemIf)->GetContent())); } catch(...) { BASE::RemoveAt(Index); m_ElemIfArray.RemoveAt(Index); return E_OUTOFMEMORY; } return S_OK; } template inline void IF_ARRAY::Delete( IN UINT Index ) { ASSERT(NULL != m_SdpList); ASSERT(BASE::GetSize() == m_SdpList->GetSize()); ASSERT(Index < (UINT)BASE::GetSize()); // inform the instance that a reference to the blob is no longer needed ((T *)m_ElemIfArray.GetAt(Index))->ClearSdpBlobRefs(); m_ElemIfArray.GetAt(Index)->Release(); // move other members backwards m_ElemIfArray.RemoveAt(Index); BASE::RemoveAt(Index); m_SdpList->RemoveAt(Index); } template inline UINT IF_ARRAY::GetSize( ) { ASSERT(0 <= BASE::GetSize()); return (UINT)BASE::GetSize(); } template inline VARIANT * IF_ARRAY::GetData( ) { return BASE::GetData(); } template inline typename IF_ARRAY::ELEM_IF ** IF_ARRAY::GetElemIfArrayData( ) { return m_ElemIfArray.GetData(); } template inline CSdpConferenceBlob * IF_ARRAY::GetSdpBlob( ) { return m_ConfBlob; } template inline void IF_ARRAY::ClearSdpBlobRefs( ) { m_ConfBlob = NULL; // clear sdp blob references in each of the inserted instances for(UINT i=0; (int)i < BASE::GetSize(); i++) { // inform the inserted instance that a reference to the blob is no longer needed ((T *)m_ElemIfArray.GetAt(i))->ClearSdpBlobRefs(); } // keep the list (m_SdpList) around, it is already disassociated from the conf blob instance } template inline typename IF_ARRAY::SDP_LIST * IF_ARRAY::GetSdpList( ) { return m_SdpList; } template IF_ARRAY::~IF_ARRAY( ) { for(UINT i=0; (int)i < BASE::GetSize(); i++) { if ( NULL != m_ElemIfArray.GetAt(i) ) { // inform the instance that a reference to the blob is no longer needed // NOTE: the Remove... call may already have been made, but since it is an // inline fn, no need to check that before calling ((T *)m_ElemIfArray.GetAt(i))->ClearSdpBlobRefs(); // NOTE: since the interface is stored twice - in the elem if array as well // as the base variant array but AddRef is only done once (by _InternalQuery..) // Release is also done only once m_ElemIfArray.GetAt(i)->Release(); } } } template class ATL_NO_VTABLE MY_COLL_IMPL : public T::COLL_IF { protected: typedef typename T::SDP_LIST SDP_LIST; typedef typename T::ELEM_IF ELEM_IF; typedef CComObject > > ENUM_VARIANT; typedef typename T::ENUM_IF ENUM_IF; typedef _CopyInterface COPY_ELEM_IF; public: inline MY_COLL_IMPL(); inline HRESULT Init( IN CSdpConferenceBlob &ConfBlob, IN SDP_LIST &SdpList ); STDMETHOD(Create)(/*[in]*/ LONG Index, /*[out, retval]*/ ELEM_IF **Interface); STDMETHOD(Delete)(/*[in]*/ LONG Index); STDMETHOD(get__NewEnum)(/*[out, retval]*/ IUnknown * *pVal); STDMETHOD(get_EnumerationIf)(/*[out, retval]*/ ENUM_IF **pVal) = 0; STDMETHOD(get_Item)(/*[in]*/ LONG Index, /*[out, retval]*/ ELEM_IF **pVal); STDMETHOD(get_Count)(/*[out, retval]*/ LONG *pVal); inline void ClearSdpBlobRefs(); virtual ~MY_COLL_IMPL(); protected: IF_ARRAY *m_IfArray; }; template inline MY_COLL_IMPL::MY_COLL_IMPL( ) : m_IfArray(NULL) { } template inline HRESULT MY_COLL_IMPL::Init( IN CSdpConferenceBlob &ConfBlob, IN SDP_LIST &SdpList ) { if ( NULL != m_IfArray ) { delete m_IfArray; } // create an interface array try { m_IfArray = new IF_ARRAY; } catch(...) { m_IfArray = NULL; } BAIL_IF_NULL(m_IfArray, E_OUTOFMEMORY); // initialize the interface array HRESULT HResult = m_IfArray->Init(ConfBlob, SdpList); BAIL_ON_FAILURE(HResult); // successful return S_OK; } template STDMETHODIMP MY_COLL_IMPL::Create( /*[in]*/ LONG Index, /*[out, retval]*/ ELEM_IF **Interface ) { CLock Lock(g_DllLock); ASSERT(NULL != m_IfArray); BAIL_IF_NULL(m_IfArray, E_FAIL); // use 1-based index, VB like // can add at atmost 1 beyond the last element if ((Index < (LONG)1) || (Index > (LONG)(m_IfArray->GetSize()+1))) { return E_INVALIDARG; } BAIL_IF_NULL(Interface, E_INVALIDARG); // if the sdp blob doesn't exist, creation is not allowed if ( NULL == m_IfArray->GetSdpBlob() ) { return HRESULT_FROM_ERROR_CODE(SDPBLB_CONF_BLOB_DESTROYED); } CComObject *TComObject; HRESULT HResult = CComObject::CreateInstance(&TComObject); BAIL_ON_FAILURE(HResult); HResult = TComObject->Init(*(m_IfArray->GetSdpBlob())); if ( FAILED(HResult) ) { delete TComObject; return HResult; } HResult = TComObject->_InternalQueryInterface(T::ELEM_IF_ID, (void**)Interface); if (FAILED(HResult)) { delete TComObject; return HResult; } // adjust index to c like index value HResult = m_IfArray->Add(Index-1, *Interface); if (FAILED(HResult)) { delete TComObject; return HResult; } // add another reference count for the interface being returned (*Interface)->AddRef(); return S_OK; } template STDMETHODIMP MY_COLL_IMPL::Delete( /*[in]*/ LONG Index ) { CLock Lock(g_DllLock); ASSERT(NULL != m_IfArray); BAIL_IF_NULL(m_IfArray, E_FAIL); // use 1-based index, VB like if ((Index < (LONG)1) || (Index > (LONG)m_IfArray->GetSize())) { return E_INVALIDARG; } // if the sdp blob doesn't exist, deletion is not allowed if ( NULL == m_IfArray->GetSdpBlob() ) { return HRESULT_FROM_ERROR_CODE(SDPBLB_CONF_BLOB_DESTROYED); } // adjust index to c like index value, delete the instance m_IfArray->Delete(Index-1); return S_OK; } template STDMETHODIMP MY_COLL_IMPL::get__NewEnum( /*[out, retval]*/ IUnknown **pVal ) { CLock Lock(g_DllLock); ASSERT(NULL != m_IfArray); BAIL_IF_NULL(m_IfArray, E_FAIL); BAIL_IF_NULL(pVal, E_INVALIDARG); ENUM_VARIANT *EnumComObject; HRESULT HResult = ENUM_VARIANT::CreateInstance(&EnumComObject); BAIL_ON_FAILURE(HResult); HResult = EnumComObject->Init( m_IfArray->GetData(), m_IfArray->GetData() + m_IfArray->GetSize(), NULL, // no owner pUnk AtlFlagCopy // copy the array data ); if ( FAILED(HResult) ) { delete EnumComObject; return HResult; } // query for the IUnknown interface and return it HResult = EnumComObject->_InternalQueryInterface(IID_IUnknown, (void**)pVal); if ( FAILED(HResult) ) { delete EnumComObject; return HResult; } return S_OK; } template STDMETHODIMP MY_COLL_IMPL::get_Item( /*[in]*/ LONG Index, /*[out, retval]*/ ELEM_IF **pVal ) { CLock Lock(g_DllLock); ASSERT(NULL != m_IfArray); BAIL_IF_NULL(m_IfArray, E_FAIL); BAIL_IF_NULL(pVal, E_INVALIDARG); // use 1-based index, VB like if ((Index < (LONG)1) || (Index > (LONG)m_IfArray->GetSize())) { return E_INVALIDARG; } *pVal = m_IfArray->GetAt(Index-1); (*pVal)->AddRef(); return S_OK; } template STDMETHODIMP MY_COLL_IMPL::get_Count( /*[out, retval]*/ LONG *pVal ) { CLock Lock(g_DllLock); ASSERT(NULL != m_IfArray); BAIL_IF_NULL(m_IfArray, E_FAIL); BAIL_IF_NULL(pVal, E_INVALIDARG); *pVal = m_IfArray->GetSize(); return S_OK; } template inline void MY_COLL_IMPL::ClearSdpBlobRefs( ) { m_IfArray->ClearSdpBlobRefs(); } template /* virtual */ MY_COLL_IMPL::~MY_COLL_IMPL( ) { // if an interface array exists, destroy it if ( NULL != m_IfArray ) { if ( NULL != m_IfArray->GetSdpList() ) { delete m_IfArray->GetSdpList(); } delete m_IfArray; } } #endif // __BLB_COLLECTION_ENUMERATION_IMPL__