/*++ Copyright (c) 1997 Microsoft Corporation Module Name: confpart.cpp Abstract: This module contains implementation of the participant classes. Author: Mu Han (muhan) 15-September-1999 --*/ #include "stdafx.h" #include "common.h" #include "confpart.h" #ifdef DEBUG_REFCOUNT ULONG CParticipant::InternalAddRef() { ULONG lRef = CComObjectRootEx::InternalAddRef(); LOG((MSP_TRACE, "%p, %s Addref, ref = %d", this, (m_InfoItems[0]) ? m_InfoItems[0] : "new participant", lRef)); return lRef; } ULONG CParticipant::InternalRelease() { ULONG lRef = CComObjectRootEx::InternalRelease(); LOG((MSP_TRACE, "%p, %s Release, ref = %d", this, (m_InfoItems[0]) ? m_InfoItems[0] : "new participant", lRef)); return lRef; } #endif CParticipant::CParticipant() : m_pFTM(NULL), m_dwSendingMediaTypes(0), m_dwReceivingMediaTypes(0) { // initialize the info item array. ZeroMemory(m_InfoItems, sizeof(char *) * (RTCP_SDES_LAST - 1)); } // methods called by the call object. HRESULT CParticipant::Init( IN char * szCName, IN ITStream * pITStream, IN DWORD dwSSRC, IN DWORD dwSendRecv, IN DWORD dwMediaType ) /*++ Routine Description: Initialize the participant object. Arguments: szCName - the canonical name of the participant. pITStream - the stream that has the participant. dwSSRC - the SSRC of the participant in that stream. dwSendRecv - a sender or a receiver. dwMediaType - the media type of the participant. Return Value: S_OK, E_OUTOFMEMORY. --*/ { LOG((MSP_TRACE, "CParticipant::Init, name:%s", szCName)); // create the marshaler. HRESULT hr; hr = CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pFTM); if (FAILED(hr)) { LOG((MSP_ERROR, "create marshaler failed, %x", hr)); return hr; } m_InfoItems[0] = (char *)malloc(lstrlenA(szCName) + 1); if (m_InfoItems[0] == NULL) { LOG((MSP_ERROR, "out of mem for CName")); return E_OUTOFMEMORY; } lstrcpyA(m_InfoItems[0], szCName); // add the stream into out list. hr = AddStream(pITStream, dwSSRC, dwSendRecv, dwMediaType); if (FAILED(hr)) { LOG((MSP_ERROR, "failed to add stream %x", hr)); return hr; } LOG((MSP_TRACE, "CParticipant: %s, Init returns S_OK", szCName)); return S_OK; } BOOL CParticipant::UpdateInfo( IN int Type, IN DWORD dwLen, IN char * szInfo ) /*++ Routine Description: Update one item of the participant info. Arguments: Type - the type of the INFO, dwLen - the length of the information. szInfo - the information. Return Value: TRUE - information changed. FALSE - the information is the same, no change was made. --*/ { int index = Type - 1; // if we have an item already, find out if it is the same. if (m_InfoItems[index] != NULL) { if (lstrcmpA(m_InfoItems[index], szInfo) == 0) { return FALSE; } // if the item is new, free the old one free(m_InfoItems[index]); } // allocate memory and store it. m_InfoItems[index] = (char *)malloc(dwLen + 1); if (m_InfoItems[index] == NULL) { return FALSE; } lstrcpynA(m_InfoItems[index], szInfo, dwLen); return TRUE; } BOOL CParticipant::UpdateSSRC( IN ITStream * pITStream, IN DWORD dwSSRC, IN DWORD dwSendRecv ) /*++ Routine Description: Update the SSRC for a stream. Arguments: pITStream - the stream that the participant is on. dwSSRC - the SSRC of the participant. dwSendRecv - the participant is a sender or a receiver. Return Value: TRUE - information changed. FALSE - the stream is not found. --*/ { CLock lock(m_lock); // if the stream is already there, update the SSRC and return. int index = m_Streams.Find(pITStream); if ( index >= 0) { m_StreamInfo[index].dwSSRC = dwSSRC; m_StreamInfo[index].dwSendRecv |= dwSendRecv; return TRUE; } return FALSE; } BOOL CParticipant::HasSSRC( IN ITStream * pITStream, IN DWORD dwSSRC ) /*++ Routine Description: find out if the participant has the SSRC for a stream. Arguments: pITStream - the stream that the participant is on. dwSSRC - the SSRC of the participant. Return Value: TRUE - the SSRC exists. FALSE - the SSRC does not exist. --*/ { CLock lock(m_lock); int index = m_Streams.Find(pITStream); if (index >= 0) { return (m_StreamInfo[index].dwSSRC == dwSSRC); } return FALSE; } BOOL CParticipant::GetSSRC( IN ITStream * pITStream, OUT DWORD * pdwSSRC ) /*++ Routine Description: Update the SSRC for a stream. Arguments: pITStream - the stream that the participant is on. pdwSSRC - the address to store the SSRC of the participant. Return Value: TRUE - the SSRC is found. FALSE - the SSRC is not found. --*/ { CLock lock(m_lock); // if the stream is already there, update the SSRC and return. int index = m_Streams.Find(pITStream); if ( index >= 0) { *pdwSSRC = m_StreamInfo[index].dwSSRC; return TRUE; } return FALSE; } DWORD CParticipant::GetSendRecvStatus( IN ITStream * pITStream ) /*++ Routine Description: find out the current send and recv status on a given stream. Arguments: pITStream - the stream that the participant is on. Return Value: A bit mask of send and receive status --*/ { CLock lock(m_lock); int index = m_Streams.Find(pITStream); if (index >= 0) { return m_StreamInfo[index].dwSendRecv; } return 0; } void CParticipant::FinalRelease() /*++ Routine Description: release everything before being deleted. Arguments: Return Value: --*/ { LOG((MSP_TRACE, "CParticipant::FinalRelease, name %s", m_InfoItems[0])); if (m_pFTM) { m_pFTM->Release(); } for (int i = 0; i < RTCP_SDES_LAST - 1; i ++) { if (m_InfoItems[i]) { free(m_InfoItems[i]); } } for (i = 0; i < m_Streams.GetSize(); i ++) { m_Streams[i]->Release(); } m_Streams.RemoveAll(); LOG((MSP_TRACE, "CParticipant::FinalRelease - exit")); } // ITParticipant methods, called by the app. STDMETHODIMP CParticipant::get_ParticipantTypedInfo( IN PARTICIPANT_TYPED_INFO InfoType, OUT BSTR * ppInfo ) /*++ Routine Description: Get a information item for this participant. Arguments: InfoType - The type of the information asked. ppInfo - the mem address to store a BSTR. Return Value: S_OK, E_INVALIDARG, E_POINTER, E_OUTOFMEMORY, TAPI_E_NOITEMS */ { LOG((MSP_TRACE, "CParticipant get info, type:%d", InfoType)); if (InfoType > PTI_PRIVATE || InfoType < 0) { LOG((MSP_ERROR, "CParticipant get info - exit invalid arg")); return E_INVALIDARG; } if (IsBadWritePtr(ppInfo, sizeof(BSTR))) { LOG((MSP_ERROR, "CParticipant get info - exit E_POINTER")); return E_POINTER; } // check if we have that info. CLock lock(m_lock); int index = (int)InfoType; if (m_InfoItems[index] == NULL) { LOG((MSP_INFO, "CParticipant get info - no item for %d", InfoType)); return TAPI_E_NOITEMS; } // conver the char string to WCHAR string. WCHAR Buffer[RTP_MAX_SDES + 1]; if (!MultiByteToWideChar( GetACP(), 0, m_InfoItems[index], -1, Buffer, RTP_MAX_SDES )) { LOG((MSP_ERROR, "coverting failed, error:%x", GetLastError())); return E_FAIL; } // make a BSTR out of it. BSTR pName = SysAllocString(Buffer); if (pName == NULL) { LOG((MSP_ERROR, "CParticipant get info - exit out of mem")); return E_OUTOFMEMORY; } // return the BSTR. *ppInfo = pName; return S_OK; } STDMETHODIMP CParticipant::get_MediaTypes( // IN TERMINAL_DIRECTION Direction, OUT long * plMediaTypes ) /*++ Routine Description: Get the media type of the participant Arguments: plMediaType - the mem address to store a long. Return Value: S_OK, E_POINTER, */ { LOG((MSP_TRACE, "CParticipant::get_MediaTypes - enter")); if (IsBadWritePtr(plMediaTypes, sizeof (long))) { LOG((MSP_ERROR, "CParticipant::get_MediaType - exit E_POINTER")); return E_POINTER; } CLock lock(m_lock); #if 0 if (Direction == TD_RENDER) { *plMediaTypes = (long)m_dwReceivingMediaTypes; } else { *plMediaTypes = (long)m_dwSendingMediaTypes; } #endif *plMediaTypes = (long)(m_dwSendingMediaTypes | m_dwReceivingMediaTypes); LOG((MSP_TRACE, "CParticipant::get_MediaType:%x - exit S_OK", *plMediaTypes)); return S_OK; } STDMETHODIMP CParticipant::put_Status( IN ITStream * pITStream, IN VARIANT_BOOL fEnable ) { /* this is a new feature, we still can not really control the participant. LOG((MSP_TRACE, "CParticipant::put_Status, pITStream %p, status %hs", pITStream, fEnable ? "Enable" : "Disable" )); HRESULT hr; // if the caller specified a stream, find the stream and use it. if (pITStream != NULL) { m_lock.Lock(); int index; if ((index = m_Streams.Find(pITStream)) < 0) { m_lock.Unlock(); LOG((MSP_ERROR, "CParticipant::put_Status, stream %p not found", pITStream,)); return E_INVALIDARG; } // add ref so that it won't go away. pITStream->AddRef(); m_lock.Unlock; hr = ((CIPConfMSPStream *)pITStream)->EnableParticipant( fEnable ); pITStream->Release(); return hr; } // if the caller didn't specify a stream, set the status on all streams. m_lock.Lock(); int nSize = m_Streams.GetSize(); ITStream ** Streams = (ITSTream **)malloc(sizeof(ITStream*) * nSize); if (Streams == NULL) { m_lock.Unlock(); LOG((MSP_ERROR, "CParticipant::put_Status out of memory")); return E_OUTOFMEMORY; } for (int i = 0; i < nSize; i ++) { Streams[i] = m_Streams[i]; Streams[i]->AddRef(); } m_lock.Unlock(); for (i = 0; i < nSize; i ++) { hr = Streams[i]->EnableParticipant(fEnable); if (FAILED(hr)) { break; } } for (i = 0; i < nSize; i ++) { Streams[i]->Release(); } free(Streams); return hr; */ return E_NOTIMPL; } STDMETHODIMP CParticipant::get_Status( IN ITStream * pITStream, OUT VARIANT_BOOL * pStatus ) { return E_NOTIMPL; } STDMETHODIMP CParticipant::EnumerateStreams( OUT IEnumStream ** ppEnumStream ) { LOG((MSP_TRACE, "EnumerateStreams entered. ppEnumStream:%x", ppEnumStream)); // // Check parameters. // if (IsBadWritePtr(ppEnumStream, sizeof(VOID *))) { LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - " "bad pointer argument - exit E_POINTER")); return E_POINTER; } // // First see if this call has been shut down. // acquire the lock before accessing the stream object list. // CLock lock(m_lock); if (m_Streams.GetData() == NULL) { LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - " "call appears to have been shut down - exit E_UNEXPECTED")); // This call has been shut down. return E_UNEXPECTED; } // // Create an enumerator object. // typedef _CopyInterface CCopy; typedef CSafeComEnum CEnumerator; HRESULT hr; CComObject *pEnum = NULL; hr = CComObject::CreateInstance(&pEnum); if (pEnum == NULL) { LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - " "Could not create enumerator object, %x", hr)); return hr; } // // query for the IID_IEnumStream i/f // hr = pEnum->_InternalQueryInterface(IID_IEnumStream, (void**)ppEnumStream); if (FAILED(hr)) { LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - " "query enum interface failed, %x", hr)); delete pEnum; return hr; } // // Init the enumerator object. The CSafeComEnum can handle zero-sized array. // hr = pEnum->Init( m_Streams.GetData(), // the begin itor m_Streams.GetData() + m_Streams.GetSize(), // the end itor, NULL, // IUnknown AtlFlagCopy // copy the data. ); if (FAILED(hr)) { LOG((MSP_ERROR, "CMSPCallBase::EnumerateStreams - " "init enumerator object failed, %x", hr)); (*ppEnumStream)->Release(); return hr; } LOG((MSP_TRACE, "CMSPCallBase::EnumerateStreams - exit S_OK")); return hr; } STDMETHODIMP CParticipant::get_Streams( OUT VARIANT * pVariant ) { LOG((MSP_TRACE, "CParticipant::get_Streams - enter")); // // Check parameters. // if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) ) { LOG((MSP_ERROR, "CParticipant::get_Streams - " "bad pointer argument - exit E_POINTER")); return E_POINTER; } // // See if this call has been shut down. Acquire the lock before accessing // the stream object list. // CLock lock(m_lock); if (m_Streams.GetData() == NULL) { LOG((MSP_ERROR, "CParticipant::get_Streams - " "call appears to have been shut down - exit E_UNEXPECTED")); // This call has been shut down. return E_UNEXPECTED; } // // create the collection object - see mspcoll.h // typedef CTapiIfCollection< ITStream * > StreamCollection; CComObject * pCollection; HRESULT hr = CComObject::CreateInstance( &pCollection ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CParticipant::get_Streams - " "can't create collection - exit 0x%08x", hr)); return hr; } // // get the Collection's IDispatch interface // IDispatch * pDispatch; hr = pCollection->_InternalQueryInterface(IID_IDispatch, (void **) &pDispatch ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CParticipant::get_Streams - " "QI for IDispatch on collection failed - exit 0x%08x", hr)); delete pCollection; return hr; } // // Init the collection using an iterator -- pointers to the beginning and // the ending element plus one. // hr = pCollection->Initialize( m_Streams.GetSize(), m_Streams.GetData(), m_Streams.GetData() + m_Streams.GetSize() ); if (FAILED(hr)) { LOG((MSP_ERROR, "CParticipant::get_Streams - " "Initialize on collection failed - exit 0x%08x", hr)); pDispatch->Release(); return hr; } // // put the IDispatch interface pointer into the variant // LOG((MSP_INFO, "CParticipant::get_Streams - " "placing IDispatch value %08x in variant", pDispatch)); VariantInit(pVariant); pVariant->vt = VT_DISPATCH; pVariant->pdispVal = pDispatch; LOG((MSP_TRACE, "CParticipant::get_Streams - exit S_OK")); return S_OK; } HRESULT CParticipant::AddStream( IN ITStream * pITStream, IN DWORD dwSSRC, IN DWORD dwSendRecv, IN DWORD dwMediaType ) /*++ Routine Description: A participant might appear on more than one streams. This function adds a new stream and the SSRC into the participant's list. Arguments: pITStream - the stream that has the participant. dwSSRC - the SSRC of the participant in that stream. dwSendRecv - the participant is a sender or receiver in the stream. dwMediaType - the media type of the stream. Return Value: S_OK, E_OUTOFMEMORY, */ { CLock lock(m_lock); // if the stream is already there, update the SSRC and return. int index = m_Streams.Find(pITStream); if ( index >= 0) { m_StreamInfo[index].dwSSRC = dwSSRC; m_StreamInfo[index].dwSendRecv |= dwSendRecv; return S_OK; } // add the stream. if (!m_Streams.Add(pITStream)) { return E_OUTOFMEMORY; } // add the SSRC and sender flag. STREAM_INFO Info; Info.dwSSRC = dwSSRC; Info.dwSendRecv = dwSendRecv; if (!m_StreamInfo.Add(Info)) { m_Streams.Remove(pITStream); return E_OUTOFMEMORY; } pITStream->AddRef(); // update the mediatype. if (dwSendRecv & PART_SEND) { m_dwSendingMediaTypes |= dwMediaType; } if (dwSendRecv & PART_RECV) { m_dwReceivingMediaTypes |= dwMediaType; } return S_OK; } HRESULT CParticipant::RemoveStream( IN ITStream * pITStream, IN DWORD dwSSRC, OUT BOOL * pbLast ) /*++ Routine Description: A participant might appear on more than one streams. This function remove a stream from the participant's list. Arguments: pITStream - the stream that has the participant. dwSSRC - the SSRC of the participant in that stream. pbLast - the memory space to store a boolean value, specifying if the stream removed was the last one in the list. Return Value: S_OK, E_POINTER, */ { CLock lock(m_lock); // first find the stream. int index = m_Streams.Find(pITStream); if (index < 0) { return E_FAIL; } if (m_Streams.GetSize() != m_StreamInfo.GetSize()) { return E_UNEXPECTED; } // then check the SSRC. if (m_StreamInfo[index].dwSSRC != dwSSRC) { // this is not the participant being looking for. return E_FAIL; } // SSRC match, we found the participant. remove the stream and info. m_Streams.RemoveAt(index); m_StreamInfo.RemoveAt(index); // release the refcount we had in the list. pITStream->Release(); // recalculate the media types. m_dwSendingMediaTypes = 0; m_dwReceivingMediaTypes = 0; for (int i = 0; i < m_Streams.GetSize(); i ++) { if (m_StreamInfo[i].dwSendRecv & PART_SEND) { m_dwSendingMediaTypes |= ((CIPConfMSPStream *)m_Streams[i])->MediaType(); } if (m_StreamInfo[i].dwSendRecv & PART_RECV) { m_dwReceivingMediaTypes |= ((CIPConfMSPStream *)m_Streams[i])->MediaType(); } } *pbLast = (m_Streams.GetSize() == 0); return S_OK; } BOOL CParticipantList::FindByCName(char *szCName, int *pIndex) const /*++ Routine Description: Find a participant by its canonical name. If the function returns true, *pIndex contains the index of the participant. If the function returns false, *pIndex contains the index where the new participant should be inserted. Arguments: szCName - the canonical name of the participant. pIndex - the memory address to store an integer. Return Value: TRUE - the participant is found. FALSE - the participant is not in the list. */ { for(int i = 0; i < m_nSize; i++) { // This list is an ordered list based on dictionary order. We are using // a linear search here, it could be changed to a binary search. // CompareCName will return 0 if the name is the same, <0 if the szCName // is bigger, >0 if the szCName is smaller. int res = ((CParticipant *)m_aT[i])->CompareCName(szCName); if(res >= 0) { *pIndex = i; return (res == 0); } } *pIndex = m_nSize; return FALSE; // not found } BOOL CParticipantList::InsertAt(int nIndex, ITParticipant *pITParticipant) /*++ Routine Description: Insert a participant into the list at a given index. Arguments: nIndex - the location where the new object is inserted. pITParticipant - the object to be inserted. Return Value: TRUE - the participant is inserted. FALSE - out of memory. */ { _ASSERTE(nIndex >= 0 && nIndex <= m_nSize); if(m_nSize == m_nAllocSize) { if (!Grow()) return FALSE; } memmove((void*)&m_aT[nIndex+1], (void*)&m_aT[nIndex], (m_nSize - nIndex) * sizeof(ITParticipant *)); m_nSize++; SetAtIndex(nIndex, pITParticipant); return TRUE; } CParticipantEvent::CParticipantEvent() : m_pFTM(NULL), m_pITParticipant(NULL), m_pITSubStream(NULL), m_Event(PE_NEW_PARTICIPANT) {} // methods called by the call object. HRESULT CParticipantEvent::Init( IN PARTICIPANT_EVENT Event, IN ITParticipant * pITParticipant, IN ITSubStream * pITSubStream ) /*++ Routine Description: Initialize the ParticipantEvent object. Arguments: Event - the event. pITParticipant - the participant. pITSubStream - the substream, can be NULL. Return Value: S_OK, --*/ { LOG((MSP_TRACE, "CParticipantEvent::Init")); // create the marshaler. HRESULT hr; hr = CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pFTM); if (FAILED(hr)) { LOG((MSP_ERROR, "create marshaler failed, %x", hr)); return hr; } m_Event = Event; m_pITParticipant = pITParticipant; if (m_pITParticipant) m_pITParticipant->AddRef(); m_pITSubStream = pITSubStream; if (m_pITSubStream) m_pITSubStream->AddRef(); LOG((MSP_TRACE, "CParticipantEvent Init returns S_OK")); return S_OK; } void CParticipantEvent::FinalRelease() /*++ Routine Description: release everything before being deleted. Arguments: Return Value: --*/ { LOG((MSP_TRACE, "CParticipantEvent::FinalRelease - enter")); if (m_pFTM) { m_pFTM->Release(); } if (m_pITParticipant) m_pITParticipant->Release(); if (m_pITSubStream) m_pITSubStream->Release(); LOG((MSP_TRACE, "CParticipantEvent::FinalRelease - exit")); } STDMETHODIMP CParticipantEvent::get_Event( OUT PARTICIPANT_EVENT * pParticipantEvent ) { if (IsBadWritePtr(pParticipantEvent, sizeof (PARTICIPANT_EVENT))) { LOG((MSP_ERROR, "CParticipantEvent::get_Event - exit E_POINTER")); return E_POINTER; } *pParticipantEvent = m_Event; return S_OK; } STDMETHODIMP CParticipantEvent::get_Participant( OUT ITParticipant ** ppITParticipant ) { if (IsBadWritePtr(ppITParticipant, sizeof (void *))) { LOG((MSP_ERROR, "CParticipantEvent::get_participant - exit E_POINTER")); return E_POINTER; } if (!m_pITParticipant) { LOG((MSP_ERROR, "CParticipantevnt::get_Participant - exit no item")); return TAPI_E_NOITEMS; } m_pITParticipant->AddRef(); *ppITParticipant = m_pITParticipant; return S_OK; } STDMETHODIMP CParticipantEvent::get_SubStream( OUT ITSubStream** ppSubStream ) { if (IsBadWritePtr(ppSubStream, sizeof (void *))) { LOG((MSP_ERROR, "CParticipantEvent::get_SubStream - exit E_POINTER")); return E_POINTER; } if (!m_pITSubStream) { LOG((MSP_WARN, "CParticipantevnt::get_SubStream - exit no item")); return TAPI_E_NOITEMS; } m_pITSubStream->AddRef(); *ppSubStream = m_pITSubStream; return S_OK; } HRESULT CreateParticipantEvent( IN PARTICIPANT_EVENT Event, IN ITParticipant * pITParticipant, IN ITSubStream * pITSubStream, OUT IDispatch ** ppIDispatch ) { // create the object. CComObject * pCOMParticipantEvent; HRESULT hr = CComObject ::CreateInstance(&pCOMParticipantEvent); if (NULL == pCOMParticipantEvent) { LOG((MSP_ERROR, "could not create participant event:%x", hr)); return hr; } IDispatch * pIDispatch; // get the interface pointer. hr = pCOMParticipantEvent->_InternalQueryInterface( IID_IDispatch, (void **)&pIDispatch ); if (FAILED(hr)) { LOG((MSP_ERROR, "Create ParticipantEvent QueryInterface failed: %x", hr)); delete pCOMParticipantEvent; return hr; } // Initialize the object. hr = pCOMParticipantEvent->Init( Event, pITParticipant, pITSubStream ); if (FAILED(hr)) { LOG((MSP_ERROR, "CreateMSPParticipantEvent:call init failed: %x", hr)); pIDispatch->Release(); return hr; } *ppIDispatch = pIDispatch; return S_OK; } HRESULT CreateParticipantEnumerator( IN ITParticipant ** begin, IN ITParticipant ** end, OUT IEnumParticipant ** ppEnumParticipant ) { // // Create an enumerator object. // typedef _CopyInterface CCopy; typedef CSafeComEnum CEnumerator; HRESULT hr; CComObject *pEnum = NULL; hr = CComObject::CreateInstance(&pEnum); if (pEnum == NULL) { LOG((MSP_ERROR, "CreateParticipantEnumerator - " "Could not create enumerator object, %x", hr)); return hr; } // // query for the IID_IEnumParticipant i/f // hr = pEnum->_InternalQueryInterface( IID_IEnumParticipant, (void**)ppEnumParticipant ); if (FAILED(hr)) { LOG((MSP_ERROR, "CreateParticipantEnumerator - " "query enum interface failed, %x", hr)); delete pEnum; return hr; } // // Init the enumerator object. The CSafeComEnum can handle zero-sized array. // hr = pEnum->Init( begin, // the begin itor end, // the end itor, NULL, // IUnknown AtlFlagCopy // copy the data. ); if (FAILED(hr)) { LOG((MSP_ERROR, "CreateParticipantEnumerator - " "init enumerator object failed, %x", hr)); (*ppEnumParticipant)->Release(); return hr; } LOG((MSP_TRACE, "CreateParticipantEnumerator - exit S_OK")); return hr; } HRESULT CreateParticipantCollection( IN ITParticipant ** begin, IN ITParticipant ** end, IN int nSize, OUT VARIANT * pVariant ) { // // create the collection object - see mspcoll.h // typedef CTapiIfCollection< ITParticipant * > ParticipantCollection; CComObject * pCollection; HRESULT hr = CComObject::CreateInstance( &pCollection ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CreateParticipantCollection - " "can't create collection - exit 0x%08x", hr)); return hr; } // // get the Collection's IDispatch interface // IDispatch * pDispatch; hr = pCollection->_InternalQueryInterface(IID_IDispatch, (void **) &pDispatch ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CreateParticipantCollection - " "QI for IDispatch on collection failed - exit 0x%08x", hr)); delete pCollection; return hr; } // // Init the collection using an iterator -- pointers to the beginning and // the ending element plus one. // hr = pCollection->Initialize(nSize, begin, end); if (FAILED(hr)) { LOG((MSP_ERROR, "CreateParticipantCollection- " "Initialize on collection failed - exit 0x%08x", hr)); pDispatch->Release(); return hr; } // // put the IDispatch interface pointer into the variant // VariantInit(pVariant); pVariant->vt = VT_DISPATCH; pVariant->pdispVal = pDispatch; LOG((MSP_TRACE, "CreateParticipantCollection - exit S_OK")); return S_OK; }