/*++ 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 E_NOTIMPL */ STDMETHODIMP CImpIConnPtCont::EnumConnectionPoints ( OUT LPENUMCONNECTIONPOINTS *ppIEnum ) { CImpIEnumConnPt *pEnum; if (ppIEnum == NULL) return E_POINTER; *ppIEnum = NULL; pEnum = new CImpIEnumConnPt(this, apIIDConnectPt, CONNECTION_POINT_CNT); if (pEnum == NULL) return E_OUTOFMEMORY; return pEnum->QueryInterface(IID_IEnumConnectionPoints, (PPVOID)ppIEnum); } /* * 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 ) { PCImpIConnectionPoint pConnPt; *ppCP=NULL; // 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 return ResultFromScode(E_NOINTERFACE); // Return the IConnectionPoint interface return pConnPt->QueryInterface(IID_IConnectionPoint, (PPVOID)ppCP); } /* * 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 ) { *ppv=NULL; if (IID_IUnknown==riid || IID_IConnectionPoint==riid) *ppv=(LPVOID)this; if (NULL != *ppv) { ((LPUNKNOWN)*ppv)->AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } 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 */ void 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(); } /* * 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 ) { if (NULL == pIID) return ResultFromScode(E_POINTER); *pIID = *apIIDConnectPt[m_iConnPtType]; return NOERROR; } /* * 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 ) { return m_pObj->QueryInterface(IID_IConnectionPointContainer, (void **)ppCPC); } /* * 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; *pdwCookie = 0; // Can only support one connection if (NULL != m_Connection.pIDirect) { hr = ResultFromScode(CONNECT_E_ADVISELIMIT); } else { // Get interface from sink if (FAILED(pUnkSink->QueryInterface(*apIIDConnectPt[m_iConnPtType], (PPVOID)&m_Connection))) hr = ResultFromScode(CONNECT_E_CANNOTCONNECT); else { // Return our cookie *pdwCookie = eAdviseKey; hr = NOERROR; } } 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 ResultFromScode(CONNECT_E_NOCONNECTION); EnterUnadvise(); m_Connection.pIDirect = NULL; ExitUnadvise(); return NOERROR; } /* * CImpIConnectionPoint::EnumConnections * * Purpose: * Not implemented because only one conection is allowed */ STDMETHODIMP CImpIConnectionPoint::EnumConnections ( OUT LPENUMCONNECTIONS *ppEnum ) { if (ppEnum == NULL) return E_POINTER; *ppEnum = NULL; return ResultFromScode(E_NOTIMPL); } /* * 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 ) { if ((riid == IID_IUnknown) || (riid == IID_IEnumConnectionPoints)) { *ppv = this; AddRef(); return NOERROR; } *ppv = NULL; return E_NOINTERFACE; } 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; hr = NOERROR; // 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 desired, return number of items fetched if (pcReturned != NULL) *pcReturned = cRet; 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 (m_uCurrent == m_cItems) ? S_FALSE : 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; *ppEnum = NULL; // Create new enumerator pNewEnum = new CImpIEnumConnPt(m_pConnPtCont, m_apIID, m_cItems); if (pNewEnum == NULL) return E_OUTOFMEMORY; // Copy current position pNewEnum->m_uCurrent = m_uCurrent; *ppEnum = pNewEnum; return NOERROR; }