/*++ Copyright (C) 1993-1999 Microsoft Corporation Module Name: iconnpt.cpp Abstract: Implementation of CImpIConnectionPoint for the Polyline object as well as CConnectionPoint. --*/ #include "polyline.h" #include "iconnpt.h" #include "unkhlpr.h" static const IID *apIIDConnectPt [CONNECTION_POINT_CNT] = { &IID_ISystemMonitorEvents, &DIID_DISystemMonitorEvents }; // // CImpIConnPt interface implementation // IMPLEMENT_CONTAINED_IUNKNOWN(CImpIConnPtCont) /* * CImpIConnPtCont::CImpIConnPtCont * * Purpose: * Constructor. * * Return Value: */ CImpIConnPtCont::CImpIConnPtCont ( PCPolyline pObj, LPUNKNOWN pUnkOuter) : m_cRef(0), m_pObj(pObj), m_pUnkOuter(pUnkOuter) { return; } /* * CImpIConnPtCont::~CImpIConnPtCont * * Purpose: * Destructor. * * Return Value: */ CImpIConnPtCont::~CImpIConnPtCont( void ) { return; } /* * CImpIConnPtCont::EnumConnectionPoints * * Purpose: * Not implemented. * * Return Value: * HRESULT Error code or S_OK */ STDMETHODIMP CImpIConnPtCont::EnumConnectionPoints ( OUT LPENUMCONNECTIONPOINTS *ppIEnum ) { CImpIEnumConnPt *pEnum; HRESULT hr = S_OK; if (ppIEnum == NULL) { return E_POINTER; } pEnum = new CImpIEnumConnPt(this, apIIDConnectPt, CONNECTION_POINT_CNT); if (pEnum == NULL) { hr = E_OUTOFMEMORY; } else { try { *ppIEnum = NULL; hr = pEnum->QueryInterface(IID_IEnumConnectionPoints, (PPVOID)ppIEnum); } catch (...) { hr = E_POINTER; } } if (FAILED(hr) && pEnum) { delete pEnum; } return hr; } /* * CImpIConnPtCont::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 CImpIConnPtCont::FindConnectionPoint ( IN REFIID riid, OUT IConnectionPoint **ppCP ) { HRESULT hr = S_OK; PCImpIConnectionPoint pConnPt = NULL; if (ppCP == NULL) { return E_POINTER; } // // if request matches one of our connection IDs // if (IID_ISystemMonitorEvents == riid) pConnPt = &m_pObj->m_ConnectionPoint[eConnectionPointDirect]; else if (DIID_DISystemMonitorEvents == riid) pConnPt = &m_pObj->m_ConnectionPoint[eConnectionPointDispatch]; else { hr = E_NOINTERFACE; } if (SUCCEEDED(hr)) { try { *ppCP=NULL; // // Return the IConnectionPoint interface // hr = pConnPt->QueryInterface(IID_IConnectionPoint, (PPVOID)ppCP); } catch (...) { hr = E_POINTER; } } return hr; } /* * CImpIConnectionPoint constructor */ CImpIConnectionPoint::CImpIConnectionPoint ( void ) : m_cRef(0), m_pObj(NULL), m_pUnkOuter(NULL), m_hEventEventSink(NULL), m_lSendEventRefCount(0), m_lUnadviseRefCount(0) { m_Connection.pIDirect = NULL; m_Connection.pIDispatch = NULL; } /* * CImpIConnectionPoint destructor */ CImpIConnectionPoint::~CImpIConnectionPoint ( void ) { DeinitEventSinkLock(); } /* * CImpIConnectionPoint::QueryInterface * CImpIConnectionPoint::AddRef * CCImpIonnectionPoint::Release * */ STDMETHODIMP CImpIConnectionPoint::QueryInterface ( IN REFIID riid, OUT LPVOID *ppv ) { HRESULT hr = S_OK; if (ppv == NULL) { return E_POINTER; } try { *ppv = NULL; if (IID_IUnknown==riid || IID_IConnectionPoint==riid) { *ppv = (PVOID)this; AddRef(); } else { hr = E_NOINTERFACE; } } catch (...) { hr = E_POINTER; } return hr; } STDMETHODIMP_(ULONG) CImpIConnectionPoint::AddRef( void ) { ++m_cRef; return m_pUnkOuter->AddRef(); } STDMETHODIMP_(ULONG) CImpIConnectionPoint::Release ( void ) { --m_cRef; return m_pUnkOuter->Release(); } /* * CImpIConnectionPoint::Init * * Purpose: * Set back-pointers and connection type. * * Paramters: * pObj Containing Object * pUnkOuter Controlling Object * iConnectType Connection point type */ BOOL CImpIConnectionPoint::Init ( IN PCPolyline pObj, IN LPUNKNOWN pUnkOuter, IN INT iConnPtType ) { DWORD dwStat = 0; m_pObj = pObj; m_pUnkOuter = pUnkOuter; m_iConnPtType = iConnPtType; dwStat = InitEventSinkLock(); if (dwStat != ERROR_SUCCESS) { return FALSE; } return TRUE; } /* * CImpIConnectionPoint::GetConnectionInterface * * Purpose: * Returns the IID of the outgoing interface supported through * this connection point. * * Parameters: * pIID IID * in which to store the IID. */ STDMETHODIMP CImpIConnectionPoint::GetConnectionInterface ( OUT IID *pIID ) { HRESULT hr = S_OK; if (pIID == NULL) { return E_POINTER; } try { *pIID = *apIIDConnectPt[m_iConnPtType]; } catch (...) { hr = E_POINTER; } return hr; } /* * CImpIConnectionPoint::GetConnectionPointContainer * * Purpose: * Returns a pointer to the IConnectionPointContainer that * is manageing this connection point. * * Parameters: * ppCPC IConnectionPointContainer ** in which to return * the pointer after calling AddRef. */ STDMETHODIMP CImpIConnectionPoint::GetConnectionPointContainer ( OUT IConnectionPointContainer **ppCPC ) { HRESULT hr = S_OK; if (ppCPC == NULL) { return E_POINTER; } try { *ppCPC = NULL; m_pObj->QueryInterface(IID_IConnectionPointContainer, (void **)ppCPC); } catch (...) { hr = E_POINTER; } return hr; } /* * CImpIConnectionPoint::Advise * * Purpose: * Provides this connection point with a notification sink to * call whenever the appropriate outgoing function/event occurs. * * Parameters: * pUnkSink LPUNKNOWN to the sink to notify. The connection * point must QueryInterface on this pointer to obtain * the proper interface to call. The connection * point must also insure that any pointer held has * a reference count (QueryInterface will do it). * pdwCookie DWORD * in which to store the connection key for * later calls to Unadvise. */ STDMETHODIMP CImpIConnectionPoint::Advise ( IN LPUNKNOWN pUnkSink, OUT DWORD *pdwCookie ) { HRESULT hr = S_OK; if (pUnkSink == NULL || pdwCookie == NULL) { return E_POINTER; } // // Can only support one connection // if (NULL != m_Connection.pIDirect) { hr = CONNECT_E_ADVISELIMIT; } else { try { *pdwCookie = 0; // // Get interface from sink // hr = pUnkSink->QueryInterface(*apIIDConnectPt[m_iConnPtType], (PPVOID)&m_Connection); if (SUCCEEDED(hr)) { // // Return our cookie // *pdwCookie = eAdviseKey; } } catch (...) { hr = E_POINTER; } } return hr; } /* * CImpIConnectionPoint::SendEvent * * Purpose: * Sends an event to the attached event sink * * Parameters: * uEventType Event code * dwParam Parameter to send with event * */ void CImpIConnectionPoint::SendEvent ( IN UINT uEventType, IN DWORD dwParam ) { // If not connected, just return. if ( EnterSendEvent() ) { if (m_Connection.pIDirect != NULL) { // For direct connection, call the method if (m_iConnPtType == eConnectionPointDirect) { switch (uEventType) { case eEventOnCounterSelected: m_Connection.pIDirect->OnCounterSelected((INT)dwParam); break; case eEventOnCounterAdded: m_Connection.pIDirect->OnCounterAdded((INT)dwParam); break; case eEventOnCounterDeleted: m_Connection.pIDirect->OnCounterDeleted((INT)dwParam); break; case eEventOnSampleCollected: m_Connection.pIDirect->OnSampleCollected(); break; case eEventOnDblClick: m_Connection.pIDirect->OnDblClick((INT)dwParam); break; } } // for dispatch connection, call Invoke else if ( m_iConnPtType == eConnectionPointDispatch ) { if ( NULL != m_Connection.pIDispatch ) { DISPPARAMS dp; VARIANT vaRet; VARIANTARG varg; VariantInit(&vaRet); if ( uEventType == eEventOnSampleCollected ) { SETNOPARAMS(dp) } else { VariantInit(&varg); V_VT(&varg) = VT_I4; V_I4(&varg) = (INT)dwParam; SETDISPPARAMS(dp, 1, &varg, 0, NULL) } m_Connection.pIDispatch->Invoke(uEventType, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dp , &vaRet, NULL, NULL); } } } } ExitSendEvent(); return; } /* * CImpIConnectionPoint::Unadvise * * Purpose: * Terminates the connection to the notification sink identified * with dwCookie (that was returned from Advise). The connection * point has to Release any held pointers for that sink. * * Parameters: * dwCookie DWORD connection key from Advise. */ STDMETHODIMP CImpIConnectionPoint::Unadvise ( IN DWORD dwCookie ) { if (eAdviseKey != dwCookie) return CONNECT_E_NOCONNECTION; EnterUnadvise(); m_Connection.pIDirect = NULL; ExitUnadvise(); return S_OK; } /* * CImpIConnectionPoint::EnumConnections * * Purpose: * Not implemented because only one conection is allowed */ STDMETHODIMP CImpIConnectionPoint::EnumConnections ( OUT LPENUMCONNECTIONS *ppEnum ) { HRESULT hr = E_NOTIMPL; if (ppEnum == NULL) { return E_POINTER; } try { *ppEnum = NULL; } catch (...) { hr = E_POINTER; } return hr; } /* * Locks for the event sink. */ DWORD CImpIConnectionPoint::InitEventSinkLock ( void ) { DWORD dwStat = 0; m_lUnadviseRefCount = 0; m_lSendEventRefCount = 0; if ( NULL == ( m_hEventEventSink = CreateEvent ( NULL, TRUE, TRUE, NULL ) ) ) dwStat = GetLastError(); return dwStat; } void CImpIConnectionPoint::DeinitEventSinkLock ( void ) { // Release the event sink lock if ( NULL != m_hEventEventSink ) { CloseHandle ( m_hEventEventSink ); m_hEventEventSink = NULL; } m_lSendEventRefCount = 0; m_lUnadviseRefCount = 0; } BOOL CImpIConnectionPoint::EnterSendEvent ( void ) { // Return value indicates whether lock is granted. // If lock is not granted, must still call ExitSendEvent. // Increment the SendEvent reference count when SendEvent is active. InterlockedIncrement( &m_lSendEventRefCount ); // Grant the lock unless the event sink pointer is being modified in Unadvise. return ( 0 == m_lUnadviseRefCount ); } void CImpIConnectionPoint::ExitSendEvent ( void ) { LONG lTemp; // Decrement the SendEvent reference count. lTemp = InterlockedDecrement( &m_lSendEventRefCount ); // Signal the event sink if SendEvent count decremented to 0. // lTemp is the value previous to decrement. if ( 0 == lTemp ) SetEvent( m_hEventEventSink ); } void CImpIConnectionPoint::EnterUnadvise ( void ) { BOOL bStatus; bStatus = ResetEvent( m_hEventEventSink ); // Increment the Unadvise reference count whenever Unadvise is active. // Whenever this is > 0, events are not fired. InterlockedIncrement( &m_lUnadviseRefCount ); // Wait until SendEvent is no longer active. while ( m_lSendEventRefCount > 0 ) { WaitForSingleObject( m_hEventEventSink, eEventSinkWaitInterval ); bStatus = ResetEvent( m_hEventEventSink ); } } void CImpIConnectionPoint::ExitUnadvise ( void ) { // Decrement the Unadvise reference count. InterlockedDecrement( &m_lUnadviseRefCount ); } CImpIEnumConnPt::CImpIEnumConnPt ( IN CImpIConnPtCont *pConnPtCont, IN const IID **ppIID, IN ULONG cItems ) { m_pConnPtCont = pConnPtCont; m_apIID = ppIID; m_cItems = cItems; m_uCurrent = 0; m_cRef = 0; } STDMETHODIMP CImpIEnumConnPt::QueryInterface ( IN REFIID riid, OUT PVOID *ppv ) { HRESULT hr = S_OK; if (ppv == NULL) { return E_POINTER; } try { *ppv = NULL; if ((riid == IID_IUnknown) || (riid == IID_IEnumConnectionPoints)) { *ppv = this; AddRef(); } else { hr = E_NOINTERFACE; } } catch (...) { hr = E_POINTER; } return hr; } STDMETHODIMP_(ULONG) CImpIEnumConnPt::AddRef ( VOID ) { return ++m_cRef; } STDMETHODIMP_(ULONG) CImpIEnumConnPt::Release( VOID ) { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } STDMETHODIMP CImpIEnumConnPt::Next( IN ULONG cItems, OUT IConnectionPoint **apConnPt, OUT ULONG *pcReturned) { ULONG i; ULONG cRet; HRESULT hr = S_OK; if (apConnPt == NULL) { return E_POINTER; } try { // // Clear the return values // for (i = 0; i < cItems; i++) { apConnPt[i] = NULL; } // Try to fill the caller's array for (cRet = 0; cRet < cItems; cRet++) { // No more, return success with false if (m_uCurrent == m_cItems) { hr = S_FALSE; break; } // Ask connection point container for next connection point hr = m_pConnPtCont->FindConnectionPoint(*m_apIID[m_uCurrent], &apConnPt[cRet]); if (FAILED(hr)) break; m_uCurrent++; } // // If failed, free the accumulated interfaces // if (FAILED(hr)) { for (i = 0; i < cRet; i++) { ReleaseInterface(apConnPt[i]); } cRet = 0; } if (pcReturned) { *pcReturned = cRet; } } catch (...) { hr = E_POINTER; } return hr; } /*** *HRESULT CImpIEnumConnPt::Skip(unsigned long) *Purpose: * Attempt to skip over the next 'celt' elements in the enumeration * sequence. * *Entry: * celt = the count of elements to skip * *Exit: * return value = HRESULT * S_OK * S_FALSE - the end of the sequence was reached * ***********************************************************************/ STDMETHODIMP CImpIEnumConnPt::Skip( IN ULONG cItems ) { m_uCurrent += cItems; if (m_uCurrent > m_cItems) { m_uCurrent = m_cItems; return S_FALSE; } return S_OK; } /*** *HRESULT CImpIEnumConnPt::Reset(void) *Purpose: * Reset the enumeration sequence back to the beginning. * *Entry: * None * *Exit: * return value = SHRESULT CODE * S_OK * ***********************************************************************/ STDMETHODIMP CImpIEnumConnPt::Reset( VOID ) { m_uCurrent = 0; return S_OK; } /*** *HRESULT CImpIEnumConnPt::Clone(IEnumVARIANT**) *Purpose: * Retrun a CPoint enumerator with exactly the same state as the * current one. * *Entry: * None * *Exit: * return value = HRESULT * S_OK * E_OUTOFMEMORY * ***********************************************************************/ STDMETHODIMP CImpIEnumConnPt::Clone ( OUT IEnumConnectionPoints **ppEnum ) { CImpIEnumConnPt *pNewEnum = NULL; HRESULT hr = S_OK; if (ppEnum == NULL) { return E_POINTER; } try { *ppEnum = NULL; // Create new enumerator pNewEnum = new CImpIEnumConnPt(m_pConnPtCont, m_apIID, m_cItems); if (pNewEnum != NULL) { // Copy current position pNewEnum->m_uCurrent = m_uCurrent; *ppEnum = pNewEnum; } else { hr = E_OUTOFMEMORY; } } catch (...) { hr = E_POINTER; } if (FAILED(hr) && pNewEnum) { delete pNewEnum; } return hr; }