// // CConnectionPoint // // Common implementation for CConnectionPoint. // // // Since EnumConnections is called so much, we have a custom // enumerator for it which is faster than CStandardEnum and which // performs fewer memory allocations. // #include "stock.h" #pragma hdrstop #include #include "ieguidp.h" #include "cnctnpt.h" class CConnectionPointEnum : public IEnumConnections { public: // IUnknown methods // virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); virtual STDMETHODIMP_(ULONG) AddRef(void); virtual STDMETHODIMP_(ULONG) Release(void); // IEnumConnections methods // STDMETHOD(Next)(ULONG ccd, LPCONNECTDATA rgcd, ULONG *pcdFetched); STDMETHOD(Skip)(ULONG ccd) { return Next(ccd, NULL, NULL); } STDMETHOD(Reset)(void) { m_iPos = 0; return S_OK; } STDMETHOD(Clone)(IEnumConnections **ppecOut); friend HRESULT CConnectionPointEnum_Create(CConnectionPoint *pcp, int iPos, IEnumConnections **pecOut); private: CConnectionPointEnum(CConnectionPoint *pcp, int iPos) : m_cRef(1), m_pcp(pcp), m_iPos(iPos) { m_pcp->AddRef(); } ~CConnectionPointEnum() { m_pcp->Release(); } int m_cRef; // refcount CConnectionPoint *m_pcp; // my dad int m_iPos; // enumeration state }; // // When we need to grow the sink array, we grow by this many. // #define GROWTH 8 // // OLE says that zero is never a valid cookie, so our cookies are // the array index biased by unity. // #define COOKIEFROMINDEX(i) ((i) + 1) #define INDEXFROMCOOKIE(dw) ((dw) - 1) // // LocalReAllocHelp behaves like IMalloc::Realloc, which // is slightly different from LocalRealloc. // // IMalloc::Realloc(NULL, 0) = return NULL // IMalloc::Realloc(pv, 0) = IMalloc::Free(pv) // IMalloc::Realloc(NULL, cb) = IMalloc::Alloc(cb) // IMalloc::Realloc(pv, cb) = LocalRealloc() // void *LocalReAllocHelp(void *pv, ULONG cb) { if (cb == 0) { if (pv) { LocalFree(pv); } return NULL; } else if (pv == NULL) { return LocalAlloc(LPTR, cb); } else { return LocalReAlloc(pv, cb, LMEM_MOVEABLE|LMEM_ZEROINIT); } } CConnectionPoint::~CConnectionPoint () { // clean up some memory stuff UnadviseAll(); if (m_rgSinks) LocalFree(m_rgSinks); } HRESULT CConnectionPoint::UnadviseAll(void) { if (m_rgSinks) { int x; for (x = 0; x < m_cSinksAlloc; x++) { ATOMICRELEASE(m_rgSinks[x]); } } return S_OK; } // // For backwards-compatibility with IE4, our superclass is // CIE4ConnectionPoint. // STDMETHODIMP CConnectionPoint::QueryInterface(REFIID riid, void **ppvObjOut) { if (IsEqualIID(riid, IID_IConnectionPoint) || IsEqualIID(riid, IID_IUnknown)) { *ppvObjOut = SAFECAST(this, IConnectionPoint *); AddRef(); return S_OK; } *ppvObjOut = NULL; return E_NOINTERFACE; } STDMETHODIMP CConnectionPoint::GetConnectionInterface(IID *piid) { *piid = *m_piid; return S_OK; } STDMETHODIMP CConnectionPoint::GetConnectionPointContainer(IConnectionPointContainer **ppCPC) { return m_punk->QueryInterface(IID_IConnectionPointContainer, (void **)ppCPC); } STDMETHODIMP CConnectionPoint::Advise(IUnknown *pUnk,DWORD *pdwCookie) { HRESULT hr; IUnknown **rgUnkNew; IUnknown *punkTgt; int i = 0; if (!pdwCookie) return E_POINTER; *pdwCookie = 0; // first, make sure everybody's got what they thinks they got hr = pUnk->QueryInterface(*m_piid, (LPVOID *)&punkTgt); if (SUCCEEDED(hr)) { #ifdef DEBUG // // If we are not an IPropertyNotifySink, then we had better // be derived from IDispatch. Try to confirm. // if (m_piid != &IID_IPropertyNotifySink) { IDispatch *pdisp; if (SUCCEEDED(pUnk->QueryInterface(IID_IDispatch, (LPVOID *)&pdisp))) { pdisp->Release(); } else { AssertMsg(0, TEXT("CConnectionPoint: IID %08x not derived from IDispatch"), m_piid->Data1); } } #endif } else { if (m_piid != &IID_IPropertyNotifySink) { // This is against spec, but raymondc is guessing that this is done // for compatibility with VB or some other scripting language that // talks IDispatch but not necessarily the IDispatch-derived // thingie that we officially speak. Since we really source // merely IDispatch::Invoke, we can satisfactorily accept any // IDispatch as a sink. hr = pUnk->QueryInterface(IID_IDispatch, (LPVOID*)&punkTgt); } } if (SUCCEEDED(hr)) { // we no longer optimize the case where there is only one sink // because it's rarely the case any more. // // If the table is full, then grow it. // if (m_cSinks >= m_cSinksAlloc) { // LocalReAllocHelp is so smart. If you realloc from NULL, it // means Alloc. What this means for us? No special cases! rgUnkNew = (IUnknown **)LocalReAllocHelp(m_rgSinks, (m_cSinksAlloc + GROWTH) * sizeof(IUnknown *)); if (!rgUnkNew) { punkTgt->Release(); // GetLastError(); return E_OUTOFMEMORY; } m_rgSinks = rgUnkNew; // // OLE does not guarantee that the new memory is zero-initialized. // ZeroMemory(&m_rgSinks[m_cSinksAlloc], GROWTH * sizeof(IUnknown *)); m_cSinksAlloc += GROWTH; } // // Look for an empty slot. There has to be one since we grew the // table if we were full. // for (i = 0; m_rgSinks[i]; i++) { ASSERT(i < m_cSinksAlloc); } ASSERT(m_rgSinks[i] == NULL); // Should've found a free slot m_rgSinks[i] = punkTgt; *pdwCookie = COOKIEFROMINDEX(i); m_cSinks++; // notify our owner that someone is connecting to us -- // they may want to hook something up at the last minute // IConnectionPointCB* pcb; if (SUCCEEDED(m_punk->QueryInterface(IID_IConnectionPointCB, (LPVOID*)&pcb))) { pcb->OnAdvise(*m_piid, m_cSinks, *pdwCookie); pcb->Release(); } } else { hr = CONNECT_E_CANNOTCONNECT; } return hr; } STDMETHODIMP CConnectionPoint::Unadvise(DWORD dwCookie) { if (!dwCookie) return S_OK; int x = INDEXFROMCOOKIE(dwCookie); // Validate the cookie. if (x >= m_cSinksAlloc || m_rgSinks[x] == NULL) return CONNECT_E_NOCONNECTION; // notify our owner that someone is disconnecting from us -- // they may want to clean up from the OnAdvise call // Perform the callback while the sink is still alive, in case // the callback wants to do some last-minute communication. // IConnectionPointCB* pcb; if (SUCCEEDED(m_punk->QueryInterface(IID_IConnectionPointCB, (LPVOID*)&pcb))) { pcb->OnUnadvise(*m_piid, m_cSinks - 1, dwCookie); pcb->Release(); } // Free up the slot. We cannot relocate any elements because that // would mess up the outstanding cookies. ATOMICRELEASE(m_rgSinks[x]); m_cSinks--; // Don't free the memory on the loss of the last sink; a new one // will probably show up soon. return S_OK; } //=--------------------------------------------------------------------------= // CConnectionPoint::EnumConnections //=--------------------------------------------------------------------------= // enumerates all current connections // // Paramters: // IEnumConnections ** - [out] new enumerator object // // Output: // HRESULT // // NOtes: STDMETHODIMP CConnectionPoint::EnumConnections(IEnumConnections **ppEnumOut) { #if 1 return CConnectionPointEnum_Create(this, 0, ppEnumOut); #else CONNECTDATA *rgConnectData = NULL; int i, cSinks; // CopyAndAddRefObject assumes that the IUnknown comes first // So does CStandardEnum COMPILETIME_ASSERT(FIELD_OFFSET(CONNECTDATA, pUnk) == 0); cSinks = 0; if (_HasSinks()) { // allocate some memory big enough to hold all of the sinks. // // Must use GlobalAlloc because CStandardEnum uses GlobalFree. // rgConnectData = (CONNECTDATA *)GlobalAlloc(GMEM_FIXED, m_cSinks * sizeof(CONNECTDATA)); if (!rgConnectData) return E_OUTOFMEMORY; // fill in the array // for (i = 0; i < m_cSinksAlloc; i++) { if (m_rgSinks[i]) { rgConnectData[cSinks].pUnk = m_rgSinks[i]; rgConnectData[cSinks].dwCookie = i + 1; cSinks++; // In case m_rgSinks gets out of sync with m_cSinks, // just stop when the array gets full. if (cSinks >= m_cSinks) { break; } } } // Make sure we found all the items we should've found ASSERT(cSinks == m_cSinks); } // create a statndard enumerator object. // *ppEnumOut = (IEnumConnections *)(IEnumGeneric *)new CStandardEnum(IID_IEnumConnections, TRUE, cSinks, sizeof(CONNECTDATA), rgConnectData, CopyAndAddRefObject); if (!*ppEnumOut) { LocalFree(rgConnectData); return E_OUTOFMEMORY; } return S_OK; #endif } // // CConnectionPoint::DoInvokeIE4 // // Calls all sinks' IDispatch::Invoke() with Cancel semantics. HRESULT CConnectionPoint::DoInvokeIE4(LPBOOL pf, LPVOID *ppv, DISPID dispid, DISPPARAMS *pdispparams) { return IConnectionPoint_InvokeWithCancel(this->CastToIConnectionPoint(), dispid, pdispparams, pf, ppv); } // // CConnectionPointEnum // HRESULT CConnectionPointEnum_Create(CConnectionPoint *pcp, int iPos, IEnumConnections **ppecOut) { *ppecOut = new CConnectionPointEnum(pcp, iPos); return *ppecOut ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP CConnectionPointEnum::QueryInterface(REFIID riid, void **ppvObjOut) { if (IsEqualIID(riid, IID_IEnumConnections) || IsEqualIID(riid, IID_IUnknown)) { *ppvObjOut = (IUnknown *)this; AddRef(); return S_OK; } *ppvObjOut = NULL; return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CConnectionPointEnum::AddRef() { return ++m_cRef; } STDMETHODIMP_(ULONG) CConnectionPointEnum::Release() { ULONG cRef = --m_cRef; if (cRef == 0) delete this; return cRef; } // // Next also doubles as Skip. If you pass a NULL output buffer, then // nothing gets copied (i.e., you're a Skip). // STDMETHODIMP CConnectionPointEnum::Next(ULONG ccd, LPCONNECTDATA rgcd, ULONG *pcdFetched) { ULONG ccdFetched = 0; while (ccdFetched < ccd) { // // Look for the next sink or the end of the array // while (m_iPos < m_pcp->m_cSinksAlloc && m_pcp->m_rgSinks[m_iPos] == NULL) { m_iPos++; } if (m_iPos >= m_pcp->m_cSinksAlloc) break; if (rgcd) { // // Copy it to the output buffer // rgcd->pUnk = m_pcp->m_rgSinks[m_iPos]; rgcd->dwCookie = COOKIEFROMINDEX(m_iPos); rgcd->pUnk->AddRef(); rgcd++; } m_iPos++; ccdFetched++; } if (pcdFetched) *pcdFetched = ccdFetched; return (ccdFetched < ccd) ? S_FALSE : S_OK; } // // Our clone enumerates the same CConnectionPoint from the same position. // STDMETHODIMP CConnectionPointEnum::Clone(IEnumConnections **ppecOut) { return CConnectionPointEnum_Create(m_pcp, m_iPos, ppecOut); }