//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1994 - 1998. // // File: conpt.cxx // // Contents: connection point / notification code for cursors // // Classes: CConnectionPointBase, CConnectionPointContainer // // History: 7 Oct 1994 Dlee Created // 12 Feb 1998 AlanW Generalized // //-------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #include #include "tabledbg.hxx" // Max. connections per connection point. Should be enough for any application! const maxConnections = 20; //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::QueryInterface, public // // Synopsis: Invokes QueryInterface on container object // // Arguments: [riid] -- interface ID // [ppvObject] -- returned interface pointer // // Returns: SCODE // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointBase::QueryInterface( REFIID riid, void **ppvObject) { SCODE sc = S_OK; if ( 0 == ppvObject ) return E_INVALIDARG; *ppvObject = 0; if ( IID_IConnectionPoint == riid ) { *ppvObject = (void *) (IConnectionPoint *) this; } else if ( IID_IUnknown == riid ) { *ppvObject = (void *) (IUnknown *) this; } else { sc = E_NOINTERFACE; } if (SUCCEEDED(sc)) AddRef(); return sc; } //QueryInterface //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::AddRef, public // // Synopsis: Increments ref. count, or delegates to containing object // // Returns: ULONG // // History: 17 Mar 1998 AlanW // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CConnectionPointBase::AddRef() { if (_pIContrUnk) return _pIContrUnk->AddRef( ); else { tbDebugOut(( DEB_NOTIFY, "conpt: addref\n" )); return InterlockedIncrement( (long *) &_cRefs ); } } //AddRef //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::Release, public // // Synopsis: Decrements ref. count, or delegates to containing object // // Returns: ULONG // // History: 17 Mar 1998 AlanW // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CConnectionPointBase::Release() { if (_pIContrUnk) return _pIContrUnk->Release( ); else { long cRefs = InterlockedDecrement((long *) &_cRefs); tbDebugOut(( DEB_NOTIFY, "conpt: release, new crefs: %lx\n", _cRefs )); // If no references, make sure container doesn't know about me anymore if ( 0 == cRefs ) { Win4Assert( 0 == _pContainer ); #if 0 // Note: no sense trying to avoid an AV for bad client code if ( 0 != _pContainer ) { // need to have been disconnected; must be an excess release // from client Disconnect(); } #endif // 0 delete this; } return cRefs; } } //Release //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::GetConnectionInterface, public // // Synopsis: returns the IID of the callback notification object // // Arguments: [piid] -- interface ID // // Returns: SCODE // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointBase::GetConnectionInterface(IID * piid) { if ( 0 == piid ) return E_POINTER; *piid = _iidSink; return S_OK; } //GetConnectionInterface //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::GetConnectionPointContainer, public // // Synopsis: returns the container that spawned the connection point // // Arguments: [ppCPC] -- returns the container // // Returns: SCODE // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointBase::GetConnectionPointContainer( IConnectionPointContainer ** ppCPC) { if ( 0 == ppCPC ) return E_POINTER; *ppCPC = 0; // if disconnected from container, can't do it. if (0 == _pContainer) return E_UNEXPECTED; _pContainer->AddRef(); *ppCPC = _pContainer; return S_OK; } //GetConnectionPointContainer //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::Advise, public // // Synopsis: Passes in the client's notification object // // Arguments: [pIUnk] -- client's notification object // [pdwCookie] -- returned pseudo-id for this advise // // Returns: SCODE // // Notes: // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointBase::Advise( IUnknown * piunkNotify, DWORD * pdwCookie) { SCODE sc = S_OK; if ( 0 != pdwCookie ) *pdwCookie = 0; if ( 0 == piunkNotify || 0 == pdwCookie ) return E_POINTER; XInterface piSink; sc = piunkNotify->QueryInterface( _iidSink, piSink.GetQIPointer() ); if (! SUCCEEDED(sc)) return CONNECT_E_CANNOTCONNECT; // If disconnected from the container, can't call GetMutex. if (0 == _pContainer) return CONNECT_E_ADVISELIMIT; CLock lock( GetMutex() ); CConnectionContext * pConnCtx = LokFindConnection( 0 ); if (0 == pConnCtx && _xaConns.Count() < maxConnections) pConnCtx = &_xaConns[_xaConns.Count()]; if (0 == pConnCtx) sc = CONNECT_E_ADVISELIMIT; else { _dwCookieGen++; Win4Assert( 0 != _dwCookieGen && 0 == LokFindConnection( _dwCookieGen ) ); pConnCtx->Set( piSink.Acquire(), _dwCookieGen, _dwDefaultSpare ); if (_pAdviseHelper) (*_pAdviseHelper) (_pAdviseHelperContext, this, pConnCtx); *pdwCookie = _dwCookieGen; } return sc; } //Advise //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::Unadvise, public // // Synopsis: Turns off an advise previously turned on with Advise() // // Arguments: [dwCookie] -- pseudo-id for this advise // // Returns: SCODE // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointBase::Unadvise( DWORD dwCookie) { SCODE sc = S_OK; CConnectionContext * pConnCtx = 0; CReleasableLock lock( GetMutex(), ( 0 != _pContainer ) ); pConnCtx = LokFindConnection( dwCookie ); if (pConnCtx) { if (_pUnadviseHelper) (*_pUnadviseHelper) ( _pUnadviseHelperContext, this, pConnCtx, lock ); pConnCtx->Release(); } if (0 == pConnCtx) { tbDebugOut(( DEB_WARN, "conpt: unknown advise cookie %x\n", dwCookie )); sc = CONNECT_E_NOCONNECTION; } return sc; } //Unadvise //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::EnumConnections, public // // Synopsis: Returns an enumerator of advises open in this connection // // Arguments: [ppEnum] -- returned enumerator // // Returns: SCODE // // Notes: The spec permits E_NOTIMPL to be returned for this. If // we chose to implement it, it's a straightforward matter of // iterating over the _xaConns array. // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointBase::EnumConnections( IEnumConnections ** ppEnum) { if ( 0 == ppEnum ) return E_POINTER; *ppEnum = 0; return E_NOTIMPL; } //EnumConnections //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::Disconnect, private // // Synopsis: Disconnect from the connection point container // // Arguments: [dwCookie] -- pseudo-id for this advise // // Returns: CConnectionContext* - a connection matching the [dwCookie] or 0 // // Notes: Should be called with the CPC lock held. Might be called // without the lock for an Unadvise after the CPC is diconnected. // // History: 30 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- void CConnectionPointBase::Disconnect( ) { Win4Assert( 0 != _pContainer ); CLock lock(GetMutex()); _pContainer->RemoveConnectionPoint( this ); _pContainer = 0; } //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::LokFindConnection, private // // Synopsis: Find a connection matching an advise cookie // // Arguments: [dwCookie] -- pseudo-id for this advise // // Returns: CConnectionContext* - a connection matching the [dwCookie] or 0 // // Notes: Should be called with the CPC lock held. Might be called // without the lock for an Unadvise after the CPC is diconnected. // // History: 10 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- CConnectionPointBase::CConnectionContext * CConnectionPointBase::LokFindConnection( DWORD dwCookie ) { for (unsigned i=0; i<_xaConns.Count(); i++) { CConnectionContext & rConnCtx = _xaConns[i]; if (rConnCtx._dwAdviseCookie == dwCookie) return &rConnCtx; } return 0; } //+------------------------------------------------------------------------- // // Method: CConnectionPointBase::LokFindActiveConnection, private // // Synopsis: Find an active advise by indexing // // Arguments: [riConn] -- index into conn. context array, updated on return // // Returns: CConnectionContext* - pointer to an active connection context, // or NULL. // // Notes: Should be called with the CPC lock held. // // History: 10 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- CConnectionPointBase::CConnectionContext * CConnectionPointBase::LokFindActiveConnection( unsigned & riConn ) { while (riConn < _xaConns.Count()) { CConnectionContext & rCtx = _xaConns[riConn]; if (rCtx._dwAdviseCookie != 0) return &rCtx; riConn++; } return 0; } ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::QueryInterface, public // // Synopsis: Invokes QueryInterface on cursor object // // Arguments: [riid] -- interface ID // [ppvObject] -- returned interface pointer // // Returns: SCODE // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointContainer::QueryInterface( REFIID riid, void **ppvObject) { if ( 0 == ppvObject ) return E_INVALIDARG; return _rControllingUnk.QueryInterface(riid, ppvObject); } //QueryInterface //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::AddRef, public // // Synopsis: Invokes AddRef on cursor object // // Returns: ULONG // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CConnectionPointContainer::AddRef() { tbDebugOut(( DEB_NOTIFY, "conptcontainer: addref\n" )); return _rControllingUnk.AddRef(); } //AddRef //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::Release, public // // Synopsis: Invokes Release on cursor object // // Returns: ULONG // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CConnectionPointContainer::Release() { tbDebugOut(( DEB_NOTIFY, "conptcontainer: release\n" )); return _rControllingUnk.Release(); } //Release //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::CConnectionPointContainer, public // // Synopsis: Constructor for connection point container class. // // Arguments: [maxConnPt] -- maximum number of connection points supported // [rUnknown] -- controlling unknown // [ErrorObject] -- reference to error object // // Notes: After construction, use AddConnectionPoint to add a connection // point into the container. // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- CConnectionPointContainer::CConnectionPointContainer( unsigned maxConnPt, IUnknown &rUnknown, CCIOleDBError & ErrorObject ) : _rControllingUnk(rUnknown), _ErrorObject( ErrorObject ), _cConnPt( 0 ) { Win4Assert( maxConnPt <= maxConnectionPoints ); } //CConnectionPointContainer //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::~CConnectionPointContainer, public // // Synopsis: Destructor for connection point container class. // // Notes: It is expected that all connection points are relased by this // time. // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- CConnectionPointContainer::~CConnectionPointContainer() { CLock lock( _mutex ); // // Release all connection points // for (unsigned i = 0; i < _cConnPt; i++) { IConnectionPoint * pIConnPt = _aConnPt[i]._pIConnPt; if ( 0 != pIConnPt ) { _aConnPt[i]._pIConnPt = 0; pIConnPt->Release(); } } } //~CConnectionPointContainer //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::FindConnectionPoint, public // // Synopsis: Finds a connection point object that supports the given // interface for callback to the client // // Arguments: [riid] -- interface ID for proposed callback // [ppPoint] -- returned connection interface pointer // // Returns: SCODE // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointContainer::FindConnectionPoint( REFIID riid, IConnectionPoint ** ppPoint) { _ErrorObject.ClearErrorInfo(); if ( 0 == ppPoint ) return E_POINTER; *ppPoint = 0; SCODE sc = S_OK; TRANSLATE_EXCEPTIONS; TRY { CLock lock( _mutex ); for (unsigned i = 0; i < _cConnPt; i++) { if ( riid == _aConnPt[i]._iidConnPt ) break; } if ( i<_cConnPt ) { Win4Assert(_aConnPt[i]._pIConnPt != 0); IConnectionPoint * pIConnPt = _aConnPt[i]._pIConnPt; *ppPoint = pIConnPt; pIConnPt->AddRef(); } else { sc = CONNECT_E_NOCONNECTION; } if (FAILED(sc)) _ErrorObject.PostHResult(sc, IID_IConnectionPointContainer); } CATCH(CException,e) { _ErrorObject.PostHResult(e.GetErrorCode(), IID_IConnectionPointContainer); sc = E_UNEXPECTED; } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //FindConnectionPoint //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::EnumConnectionPoints, public // // Synopsis: Enumerates all connection points currently in use // // Arguments: [ppEnum] -- returned enumerator // // Returns: SCODE // // History: 09 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- STDMETHODIMP CConnectionPointContainer::EnumConnectionPoints( IEnumConnectionPoints **ppEnum) { _ErrorObject.ClearErrorInfo(); if ( 0 == ppEnum ) return E_POINTER; *ppEnum = 0; SCODE sc = S_OK; TRANSLATE_EXCEPTIONS; TRY { CLock lock( _mutex ); XInterface pEnumCp( new CEnumConnectionPoints( *this ) ); *ppEnum = pEnumCp.GetPointer(); pEnumCp.Acquire(); } CATCH(CException,e) { _ErrorObject.PostHResult(e.GetErrorCode(), IID_IConnectionPointContainer); sc = e.GetErrorCode(); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //EnumConnectionPoints //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::QueryInterface, public // // Synopsis: Invokes QueryInterface on connection point enumerator object // // Arguments: [riid] -- interface ID // [ppvObject] -- returned interface pointer // // Returns: SCODE // // History: 10 Mar 1998 AlanW // //-------------------------------------------------------------------------- STDMETHODIMP CEnumConnectionPoints::QueryInterface( REFIID riid, void **ppvObject) { SCODE sc = S_OK; if ( 0 == ppvObject ) return E_INVALIDARG; *ppvObject = 0; if ( IID_IEnumConnectionPoints == riid ) { *ppvObject = (void *) (IEnumConnectionPoints *) this; } else if ( IID_IUnknown == riid ) { *ppvObject = (void *) (IUnknown *) this; } else { sc = E_NOINTERFACE; } if (SUCCEEDED(sc)) AddRef(); return sc; } //QueryInterface //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::AddRef, public // // Synopsis: Invokes AddRef on container object // // Returns: ULONG // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CEnumConnectionPoints::AddRef() { return InterlockedIncrement( (long *) &_cRefs ); } //AddRef //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::Release, public // // Synopsis: Invokes Release on container object // // Returns: ULONG // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CEnumConnectionPoints::Release() { long cRefs = InterlockedDecrement((long *) &_cRefs); tbDebugOut(( DEB_NOTIFY, "enumconpt: release, new crefs: %lx\n", _cRefs )); // If no references, delete. if ( 0 == cRefs ) { delete this; } return cRefs; } //Release //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::Clone, public // // Synopsis: Clone a connection point enumerator // // Arguments: [ppEnum] -- returned enumerator // // Returns: SCODE // // History: 09 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- STDMETHODIMP CEnumConnectionPoints::Clone ( IEnumConnectionPoints **ppEnum) { //_ErrorObject.ClearErrorInfo(); if ( 0 == ppEnum ) return E_POINTER; *ppEnum = 0; SCODE sc = S_OK; TRANSLATE_EXCEPTIONS; TRY { XInterface pEnumCp( new CEnumConnectionPoints( _rContainer ) ); pEnumCp->_iConnPt = _iConnPt; *ppEnum = pEnumCp.GetPointer(); pEnumCp.Acquire(); } CATCH(CException,e) { sc = e.GetErrorCode(); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //Clone //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::Reset, public // // Synopsis: Reset a connection point enumerator // // Arguments: -NONE- // // Returns: SCODE // // History: 09 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- STDMETHODIMP CEnumConnectionPoints::Reset ( ) { _iConnPt = 0; return S_OK; } //Reset //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::Skip, public // // Synopsis: Skip some connection points // // Arguments: [cConnections] - number of connection points to skip // // Returns: SCODE // // History: 09 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- STDMETHODIMP CEnumConnectionPoints::Skip ( ULONG cConnections ) { SCODE sc = S_OK; if ( _iConnPt+cConnections < _rContainer._cConnPt ) _iConnPt += cConnections; else sc = S_FALSE; return sc; } //Skip //+------------------------------------------------------------------------- // // Method: CEnumConnectionPoints::Next, public // // Synopsis: Return some connection points // // Arguments: [cConnections] - number of connection points to return, at most // [rgpcm] - array of IConnectionPoint* to be returned // [pcFetched] - on return, number of connection points in [rgpcm] // // Returns: SCODE // // History: 09 Mar 1998 AlanW Created // //-------------------------------------------------------------------------- STDMETHODIMP CEnumConnectionPoints::Next ( ULONG cConnections, IConnectionPoint **rgpcm, ULONG * pcFetched ) { SCODE sc = S_OK; if ( 0 != pcFetched ) *pcFetched = 0; if ( 0 == rgpcm || 0 == pcFetched ) return E_POINTER; for (ULONG i=0; i xCP( _rContainer._aConnPt[_iConnPt]._pIConnPt ); xCP->AddRef(); rgpcm[cRet] = xCP.GetPointer(); cRet++; _iConnPt++; *pcFetched = cRet; xCP.Acquire(); } sc = (cConnections == cRet) ? S_OK : S_FALSE; } CATCH(CException,e) { sc = e.GetErrorCode(); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //Next //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::AddConnectionPoint, public // // Synopsis: Adds a connection point to the container. // Called by the connection point itself. // // Arguments: [riid] --- IID of notification interface for CP // [pConnPt] --- connection point to be removed // // Notes: The back pointer from the connection point to the connection // point container does not contribute to the container's ref. // count so that the CPC <-> CP structure is not self-referential // and can be deleted when no longer needed. // // History: 10 Mar 1998 AlanW // //-------------------------------------------------------------------------- void CConnectionPointContainer::AddConnectionPoint( REFIID riid, CConnectionPointBase *pConnPt) { Win4Assert( _cConnPt < maxConnectionPoints ); XInterface xConnPt; SCODE sc = pConnPt->QueryInterface( IID_IConnectionPoint, xConnPt.GetQIPointer()); if (!SUCCEEDED(sc)) THROW( CException(sc) ); CLock lock(_mutex); CConnectionPointContext * pConnPtCtx = &_aConnPt[_cConnPt]; pConnPtCtx->_pIConnPt = xConnPt.Acquire(); pConnPtCtx->_iidConnPt = riid; _cConnPt++; } //AddConnectionPoint //+------------------------------------------------------------------------- // // Method: CConnectionPointContainer::RemoveConnectionPoint, public // // Synopsis: Removes a connection point from the container. // Called by the connection point itself. // // Arguments: [pConnPt] --- connection point to be removed // // History: 07-Oct-1994 dlee // //-------------------------------------------------------------------------- void CConnectionPointContainer::RemoveConnectionPoint( IConnectionPoint *pConnPt) { CLock lock(_mutex); for (unsigned i = 0; i < _cConnPt; i++) { if ( _aConnPt[i]._pIConnPt == pConnPt ) { _aConnPt[i]._pIConnPt = 0; pConnPt->Release(); } } } //RemoveConnectionPoint