461 lines
12 KiB
C++
461 lines
12 KiB
C++
//
|
|
// 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 <olectl.h>
|
|
#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);
|
|
}
|
|
|