// This is a part of the ActiveX Template Library. // Copyright (C) 1996 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // ActiveX Template Library Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // ActiveX Template Library product. #ifndef __ATLCOM_H__ #define __ATLCOM_H__ #ifndef __cplusplus #error ATL requires C++ compilation (use a .cpp suffix) #endif #ifndef __ATLBASE_H__ #error atlcom.h requires atlbase.h to be included first #endif #pragma pack(push, _ATL_PACKING) inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return ( ((PLONG) &rguid1)[0] == ((PLONG) &rguid2)[0] && ((PLONG) &rguid1)[1] == ((PLONG) &rguid2)[1] && ((PLONG) &rguid1)[2] == ((PLONG) &rguid2)[2] && ((PLONG) &rguid1)[3] == ((PLONG) &rguid2)[3]); } ///////////////////////////////////////////////////////////////////////////// // Smart OLE pointers provide automatic AddRef/Release // CComPtr p; template class CComPtr { public: CComPtr() {p=NULL;} CComPtr(T* p_){Init(p_);} ~CComPtr() { Release(); } void Release() {if (p) p->Release();} void Init(LPUNKNOWN lp) {if ((p = lp) != NULL) p->AddRef();} LPUNKNOWN Assign(LPUNKNOWN lp); operator T*() { return (T*)p; } operator int() { return (int)p;} T& operator*() { _ASSERTE(p!=NULL); return *(T*)p; } T** operator&() {Release(); p = NULL; return (T**)&p; } T* operator->() { _ASSERTE(p!=NULL); return (T*)p; } T* operator=(T* p_) { return (T*)Assign(p_);} protected: LPUNKNOWN p; }; template LPUNKNOWN CComPtr::Assign(LPUNKNOWN lp) { if (lp != NULL) lp->AddRef(); if (p) p->Release(); p = lp; return p; } ///////////////////////////////////////////////////////////////////////////// // COM Objects typedef HRESULT (PASCAL *_ATL_CREATORFUNC)(LPUNKNOWN pUnk, REFIID riid, LPVOID* ppv); template class CComSimpleCreator { public: static HRESULT PASCAL CreateInstance(LPUNKNOWN pUnk, REFIID riid, LPVOID* ppv) { T1* p = new T1(pUnk); HRESULT hRes = p->QueryInterface(riid, ppv); if (hRes != S_OK) delete p; return hRes; } }; template class CComNoAggCreator { public: static HRESULT PASCAL CreateInstance(LPUNKNOWN pUnk, REFIID riid, LPVOID* ppv) { if (pUnk == NULL) { T1* p = new T1; HRESULT hRes = p->QueryInterface(riid, ppv); if (hRes != S_OK) delete p; return hRes; } else { *ppv = NULL; return CLASS_E_NOAGGREGATION; } } }; template class CComAggCreator { public: static HRESULT PASCAL CreateInstance(LPUNKNOWN pUnk, REFIID riid, LPVOID* ppv) { if (pUnk == NULL) { T1* p = new T1; HRESULT hRes = p->QueryInterface(riid, ppv); if (hRes != S_OK) delete p; return hRes; } else { T2* p = new T2(pUnk); HRESULT hRes = p->QueryInterface(riid, ppv); if (hRes != S_OK) delete p; return hRes; } } }; #define DECLARE_NOT_AGGREGATABLE(x) public:\ typedef CComNoAggCreator< CComObject > _CreatorClass; #define DECLARE_AGGREGATABLE(x) public:\ typedef CComAggCreator< CComObject, CComAggObject > _CreatorClass; struct _ATL_INTMAP_ENTRY { const IID* piid; // the interface id (IID) (NULL for aggregate) DWORD dw; enum Flags {offset, creator, aggregate} m_flag; }; #define BEGIN_COM_MAP(x) public:\ typedef x _atl_classtype;\ HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject)\ {return InternalQueryInterface(this, _GetEntries(), iid, ppvObject);}\ const _ATL_INTMAP_ENTRY* _GetEntries() {\ static const _ATL_INTMAP_ENTRY _entries[] = { #define COM_INTERFACE_ENTRY(x) {&IID_##x, (DWORD)((x*)((_atl_classtype*)8))-8, _ATL_INTMAP_ENTRY::offset}, #define COM_INTERFACE_ENTRY_IID(iid, x) {&iid, (DWORD)((x*)((_atl_classtype*)8))-8, _ATL_INTMAP_ENTRY::offset}, #define COM_INTERFACE_ENTRY2(x, x2) {&IID_##x, (DWORD)((x*)(x2*)((_atl_classtype*)8))-8, _ATL_INTMAP_ENTRY::offset}, #define COM_INTERFACE_ENTRY2_IID(iid, x, x2) {&iid, (DWORD)((x*)(x2*)((_atl_classtype*)8))-8, _ATL_INTMAP_ENTRY::offset}, #define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) {&iid, (DWORD)(_ATL_CREATORFUNC)CComSimpleCreator< CComTearOffObject >::CreateInstance, _ATL_INTMAP_ENTRY::creator}, #define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) {&iid, (DWORD)offsetof(_atl_classtype, punk), _ATL_INTMAP_ENTRY::aggregate}, #define END_COM_MAP() {NULL, 0, _ATL_INTMAP_ENTRY::offset}};\ return _entries;} struct _ATL_OBJMAP_ENTRY { const CLSID* pclsid; _ATL_CREATORFUNC pFunc; LPCTSTR lpszProgID; LPCTSTR lpszVerIndProgID; UINT nDescID; DWORD dwFlags; IClassFactory* pCF; DWORD dwRegister; // cookie returned by CoRegisterClassObject //Methods void UpdateRegistry(HINSTANCE hInst, HINSTANCE hInstResource); void RemoveRegistry(); HRESULT RegisterClassObject(DWORD dwClsContext, DWORD dwFlags); HRESULT RevokeClassObject() {return dwRegister ? CoRevokeClassObject(dwRegister) : E_INVALIDARG;} }; #define BEGIN_OBJECT_MAP(x) static _ATL_OBJMAP_ENTRY x[] = { #define RAWOBJECT_ENTRY(iid, pf, p1, p2, id, dw) {&iid, pf, _T(p1), _T(p2), id, dw, NULL, (DWORD)0}, #define END_OBJECT_MAP() {NULL, 0}}; #if defined(_WINDLL) | defined(_USRDLL) #define OBJECT_ENTRY(iid, class, p1, p2, id, dw) {&iid, (_ATL_CREATORFUNC)&(CComNoAggCreator< CComObject< CComClassFactory > >::CreateInstance), _T(p1), _T(p2), id, dw, NULL, (DWORD)0}, #define FACTORYOBJECT_ENTRY(iid, factory, p1, p2, id, dw) {&iid, (_ATL_CREATORFUNC)&(CComNoAggCreator< CComObject< factory > >::CreateInstance), _T(p1), _T(p2), id, dw, NULL, (DWORD)0}, #else // don't let class factory refcount influence lock count #define OBJECT_ENTRY(iid, class, p1, p2, id, dw) {&iid, (_ATL_CREATORFUNC)&(CComNoAggCreator< CComObjectNoLock< CComClassFactory > >::CreateInstance), _T(p1), _T(p2), id, dw, NULL, (DWORD)0}, #define FACTORYOBJECT_ENTRY(iid, factory, p1, p2, id, dw) {&iid, (_ATL_CREATORFUNC)&(CComNoAggCreator< CComObjectNoLock< factory > >::CreateInstance), _T(p1), _T(p2), id, dw, NULL, (DWORD)0}, #endif #define THREADFLAGS_APARTMENT 0x1 #define THREADFLAGS_BOTH 0x2 #define AUTPRXFLAG 0x4 #define CONTROLFLAG 0x8 // the functions in this class don't need to be virtual because // they are called from CComObject class CComObjectRoot { public: CComObjectRoot() {m_dwRef = 0L;} void FinalConstruct() {} ULONG InternalAddRef() {return CComThreadModel::Increment(&m_dwRef);} ULONG InternalRelease() { if (m_dwRef == 0) return 0; return CComThreadModel::Decrement(&m_dwRef); } static HRESULT PASCAL InternalQueryInterface(void* pThis, const _ATL_INTMAP_ENTRY* entries, REFIID iid, void** ppvObject); //Outer funcs ULONG OuterAddRef() {return m_pOuterUnknown->AddRef();} ULONG OuterRelease() {return m_pOuterUnknown->Release();} HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject) {return m_pOuterUnknown->QueryInterface(iid, ppvObject);} #ifndef ATL_NOEXTENDEDERROR static HRESULT PASCAL Error(const CLSID& clsid, LPCOLESTR lpszDesc, const IID& iid = GUID_NULL, HRESULT hRes = 0); static HRESULT PASCAL Error(const CLSID& clsid, LPCSTR lpszDesc, const IID& iid = GUID_NULL, HRESULT hRes = 0); static HRESULT PASCAL Error(const CLSID& clsid, HINSTANCE hInst, UINT nID, const IID& iid, HRESULT hRes = 0); #endif protected: union { long m_dwRef; IUnknown* m_pOuterUnknown; }; }; template class CComObjectBase : public CComObjectRoot { public: static HRESULT PASCAL Error(LPCOLESTR lpszDesc, const IID& iid = GUID_NULL, HRESULT hRes = 0) {return CComObjectRoot::Error(*pclsid, lpszDesc, iid, hRes);} static HRESULT PASCAL Error(LPCSTR lpszDesc, const IID& iid = GUID_NULL, HRESULT hRes = 0) {return CComObjectRoot::Error(*pclsid, lpszDesc, iid, hRes);} static HRESULT PASCAL Error(UINT nDescID, const IID& iid, HRESULT hRes = 0) {return CComObjectRoot::Error(*pclsid, _Module.GetModuleInstance(), nDescID, iid, hRes);} }; template class CComTearOffObjectBase : public CComObjectBase { public: CComObject* m_pOwner; }; //Base is the user's class that derives from CComObjectRoot and whatever //interfaces the user wants to support on the object template class CComObject : public Base { public: CComObject(LPUNKNOWN = NULL) { _Module.Lock(); //If you get a message that this call is ambiguous then you need to // override it in your class and call each base class' version of this FinalConstruct(); } ~CComObject(){_Module.Unlock();} //If InternalAddRef or InteralRelease is undefined then your class //doesn't derive from CComObjectBase STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return _InternalQueryInterface(iid, ppvObject);} }; //Base is the user's class that derives from CComObjectRoot and whatever //interfaces the user wants to support on the object template class CComObjectNoLock : public Base { public: //If you get a message that this call is ambiguous then you need to // override it in your class and call each base class' version of this CComObjectNoLock(LPUNKNOWN p = NULL){FinalConstruct();} //If InternalAddRef or InteralRelease is undefined then your class //doesn't derive from CComObjectBase STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return _InternalQueryInterface(iid, ppvObject);} }; // It is possible for Base not to derive from CComObjectRoot in this case // in order to avoid the 4 bytes for a refcount. // However, you will need to provide FinalConstruct and InternalQueryInterface template class CComObjectGlobal : public Base { public: CComObjectGlobal(LPUNKNOWN p = NULL){FinalConstruct();} STDMETHOD_(ULONG, AddRef)() {return _Module.Lock();} STDMETHOD_(ULONG, Release)(){return _Module.Unlock();} STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return _InternalQueryInterface(iid, ppvObject);} }; //Base is the user's class that derives from CComObjectRoot and whatever //interfaces the user wants to support on the object //Owner is the class of object that Base is a tear-off for template class CComTearOffObject : public Base { public: CComTearOffObject(void* p) { m_pOwner = reinterpret_cast*>(p); m_pOwner->AddRef(); //If you get a message that this call is ambiguous then you need to // override it in your class and call each base class' version of this FinalConstruct(); } ~CComTearOffObject(){m_pOwner->Release();} //If InternalAddRef or InteralRelease is undefined then your class //doesn't derive from CComObjectBase STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { if (InlineIsEqualGUID(iid, IID_IUnknown) || FAILED(_InternalQueryInterface(iid, ppvObject))) { return m_pOwner->QueryInterface(iid, ppvObject); } return S_OK; } }; template //Base must be derived from CComObjectRoot class CComContainedObject : public Base { public: CComContainedObject(LPUNKNOWN lpUnk) {m_pOuterUnknown = lpUnk;} STDMETHOD_(ULONG, AddRef)() {return OuterAddRef();} STDMETHOD_(ULONG, Release)() {return OuterRelease();} STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return OuterQueryInterface(iid, ppvObject);} protected: friend class CComAggObject; }; //contained is the user's class that derives from CComObjectRoot and whatever //interfaces the user wants to support on the object template class CComAggObject : public IUnknown, public CComObjectRoot { public: CComAggObject(LPUNKNOWN lpUnk) : m_contained(lpUnk) {_Module.Lock();m_contained.FinalConstruct();} ~CComAggObject(){_Module.Unlock();} STDMETHOD_(ULONG, AddRef)() {return CComThreadModel::Increment(&m_dwRef);} STDMETHOD_(ULONG, Release)() { if (m_dwRef == 0) return 0; LONG l = CComThreadModel::Decrement(&m_dwRef); if (l==0) delete this; return l; } STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { if (InlineIsEqualGUID(iid, IID_IUnknown)) { *ppvObject = this; AddRef(); } else return m_contained._InternalQueryInterface(iid, ppvObject); return NOERROR; } protected: CComContainedObject m_contained; }; class CComClassFactoryBase : public IClassFactory, public CComObjectRoot { public: BEGIN_COM_MAP(CComClassFactoryBase) COM_INTERFACE_ENTRY(IClassFactory) END_COM_MAP() STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj); STDMETHOD(LockServer)(BOOL fLock); STDMETHOD(implCreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)=0; }; template class CComClassFactory : public CComClassFactoryBase { public: STDMETHOD(implCreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj) { return impl::_CreatorClass::CreateInstance(pUnkOuter, riid, ppvObj); } }; // ATL doesn't support multiple LCID's at the same time // Whatever LCID is queried for first is the one that is used. class CComTypeInfoHolder { // Should be 'protected' but can cause compiler to generate fat code. public: GUID* m_pguid; GUID* m_plibid; WORD m_wMajor; WORD m_wMinor; ITypeInfo* m_pInfo; long m_dwRef; public: // GetTI doesn't refcount HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo); void AddRef(); void Release(); HRESULT GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); HRESULT GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid); HRESULT Invoke(IDispatch* p, DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr); }; template class CComDualImpl : public T { public: CComDualImpl() {_tih.AddRef();} ~CComDualImpl() {_tih.Release();} STDMETHOD(GetTypeInfoCount)(UINT* pctinfo) {*pctinfo = 1; return S_OK;} STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) {return _tih.GetTypeInfo(itinfo, lcid, pptinfo);} STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) {return _tih.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);} STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) {return _tih.Invoke((IDispatch*)this, dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);} protected: static CComTypeInfoHolder _tih; static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo) {return _tih.GetTI(lcid, ppInfo);} }; template CComTypeInfoHolder CComDualImpl::_tih = { piid, plibid, wMajor, wMinor, NULL, 0}; ///////////////////////////////////////////////////////////////////////////// // CISupportErrorInfo template class CComISupportErrorInfoImpl : public ISupportErrorInfo { STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)\ {return (InlineIsEqualGUID(riid,*piid)) ? S_OK : S_FALSE;} }; ///////////////////////////////////////////////////////////////////////////// // CComEnumImpl // These _CopyXXX classes are used with enumerators in order to control // how enumerated items are initialized, copied, and deleted // Default is shallow copy with no special init or cleanup template class _Copy { public: static void copy(T* p1, T* p2) {memcpy(p1, p2, sizeof(T));} static void init(T*) {} static void destroy(T*) {} }; class _Copy { public: static void copy(VARIANT* p1, VARIANT* p2) {VariantCopy(p1, p2);} static void init(VARIANT* p) {VariantInit(p);} static void destroy(VARIANT* p) {VariantClear(p);} }; class _Copy { public: static void copy(CONNECTDATA* p1, CONNECTDATA* p2) { *p1 = *p2; if (p1->pUnk) p1->pUnk->AddRef(); } static void init(CONNECTDATA* ) {} static void destroy(CONNECTDATA* p) {if (p->pUnk) p->pUnk->Release();} }; template class _CopyInterface { public: static void copy(T** p1, T** p2) {*p1 = *p2;if (*p1) (*p1)->AddRef();} static void init(T** ) {} static void destroy(T** p) {if (*p) (*p)->Release();} }; template class CComIEnum : public IUnknown { public: STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched) = 0; STDMETHOD(Skip)(ULONG celt) = 0; STDMETHOD(Reset)(void) = 0; STDMETHOD(Clone)(CComIEnum** ppEnum) = 0; }; template class CComEnumImpl : public Base { public: CComEnumImpl() {m_begin = m_end = m_iter = NULL; m_dwFlags = 0; m_pUnk = NULL;} ~CComEnumImpl(); STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched); STDMETHOD(Skip)(ULONG celt); STDMETHOD(Reset)(void){m_iter = m_begin;return S_OK;} STDMETHOD(Clone)(Base** ppEnum); void Init(T* begin, T* end, IUnknown* pUnk, BOOL bCopy = FALSE, BOOL bNoInitialUnkAddRef = FALSE); IUnknown* m_pUnk; T* m_begin; T* m_end; T* m_iter; DWORD m_dwFlags; enum Flags { FlagCopy = 1, FlagNoInitialUnkAddRef = 2 }; }; template CComEnumImpl::~CComEnumImpl() { if (m_dwFlags & FlagCopy) { for (T* p = m_begin; p != m_end; p++) Copy::destroy(p); delete [] m_begin; } if (m_pUnk && !(m_dwFlags & FlagNoInitialUnkAddRef)) m_pUnk->Release(); } template STDMETHODIMP CComEnumImpl::Next(ULONG celt, T* rgelt, ULONG* pceltFetched) { if (rgelt == NULL || (celt != 1 && pceltFetched == NULL)) return E_POINTER; if (m_begin == NULL || m_end == NULL || m_iter == NULL) return E_FAIL; ULONG nRem = (ULONG)(m_end - m_iter); HRESULT hRes = S_OK; if (nRem < celt) hRes = S_FALSE; ULONG nMin = min(celt, nRem); if (pceltFetched != NULL) *pceltFetched = nMin; while(nMin--) Copy::copy(rgelt++, m_iter++); return hRes; } template STDMETHODIMP CComEnumImpl::Skip(ULONG celt) { m_iter += celt; if (m_iter < m_end) return S_OK; m_iter = m_end; return S_FALSE; } template STDMETHODIMP CComEnumImpl::Clone(Base** ppEnum) { typedef CComObject > _class; HRESULT hRes = E_POINTER; if (ppEnum != NULL) { _class* p = new _class; if (p == NULL) { *ppEnum = NULL; hRes = E_OUTOFMEMORY; } else { p->Init(m_begin, m_end, (m_dwFlags & FlagCopy) ? this : m_pUnk); p->m_iter = m_iter; hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum); if (FAILED(hRes)) delete p; } } return hRes; } template void CComEnumImpl::Init(T* begin, T* end, IUnknown* pUnk, BOOL bCopy, BOOL bNoInitialUnkAddRef) { _ASSERTE(!bNoInitialUnkAddRef || (pUnk!=NULL)); if (bCopy) { m_begin = new T[end-begin]; m_iter = m_begin; for (T* i=begin; i != end; i++) { Copy::init(m_iter); Copy::copy(m_iter++, i); } m_end = m_begin + (end-begin); } else { m_begin = begin; m_end = end; } m_pUnk = pUnk; if (m_pUnk && !bNoInitialUnkAddRef) m_pUnk->AddRef(); m_iter = m_begin; if (bCopy) m_dwFlags = FlagCopy; if (bNoInitialUnkAddRef) m_dwFlags |= FlagNoInitialUnkAddRef; } template class CComEnum : public CComEnumImpl, public CComObjectRoot { public: typedef CComEnum _atl_classtype; HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) { static const _ATL_INTMAP_ENTRY _entries[] = { {piid, 0, _ATL_INTMAP_ENTRY::offset}, {NULL, 0, _ATL_INTMAP_ENTRY::offset} }; return InternalQueryInterface(this, _entries, iid, ppvObject); } }; #ifndef ATL_NOCONNPTS ///////////////////////////////////////////////////////////////////////////// // Connection Points #define BEGIN_CONNECTION_POINT_MAP()\ virtual const IID** _GetConnMap() {\ static const IID* _entries[] = { #define CONNECTION_POINT_ENTRY(x) &IID_##x, #define END_CONNECTION_POINT_MAP() NULL}; return _entries;} #ifndef _DEFAULT_VECTORLENGTH #define _DEFAULT_VECTORLENGTH 4 #endif template class CComStaticArrayCONNECTDATA { public: CComStaticArrayCONNECTDATA() { memset(m_arr, 0, sizeof(CONNECTDATA)*nMaxSize); m_pCurr = &m_arr[0]; } BOOL Add(IUnknown* pUnk); BOOL Remove(DWORD dwCookie); CONNECTDATA* begin() {return &m_arr[0];} CONNECTDATA* end() {return &m_arr[nMaxSize];} protected: CONNECTDATA m_arr[nMaxSize]; CONNECTDATA* m_pCurr; }; template inline BOOL CComStaticArrayCONNECTDATA::Add(IUnknown* pUnk) { for (CONNECTDATA* p = begin();ppUnk == NULL) { p->pUnk = pUnk; p->dwCookie = (DWORD)pUnk; return TRUE; } } return FALSE; } template inline BOOL CComStaticArrayCONNECTDATA::Remove(DWORD dwCookie) { CONNECTDATA* p; for (p=begin();pdwCookie == dwCookie) { p->pUnk = NULL; p->dwCookie = NULL; return TRUE; } } return FALSE; } class CComStaticArrayCONNECTDATA<1> { public: CComStaticArrayCONNECTDATA() {m_cd.pUnk = NULL; m_cd.dwCookie = 0;} BOOL Add(IUnknown* pUnk) { if (m_cd.pUnk != NULL) return FALSE; m_cd.pUnk = pUnk; m_cd.dwCookie = (DWORD)pUnk; return TRUE; } BOOL Remove(DWORD dwCookie) { if (dwCookie != m_cd.dwCookie) return FALSE; m_cd.pUnk = NULL; m_cd.dwCookie = 0; return TRUE; } CONNECTDATA* begin() {return &m_cd;} CONNECTDATA* end() {return (&m_cd)+1;} protected: CONNECTDATA m_cd; }; class CComDynamicArrayCONNECTDATA { public: CComDynamicArrayCONNECTDATA() { m_nSize = 0; m_pCD = NULL; } ~CComDynamicArrayCONNECTDATA() {free(m_pCD);} BOOL Add(IUnknown* pUnk); BOOL Remove(DWORD dwCookie); CONNECTDATA* begin() { CONNECTDATA* p; switch(m_nSize) { case 0: case 1: p = &m_cd; p = &m_cd; break; default: p = m_pCD; break; } return p; } CONNECTDATA* end() { CONNECTDATA* p; switch(m_nSize) { case 0: p = &m_cd; break; case 1: p = (&m_cd)+1; break; default: p = &m_pCD[m_nSize]; break; } return p; } protected: CONNECTDATA* m_pCD; CONNECTDATA m_cd; int m_nSize; }; class CComConnectionPointBase : public IConnectionPoint { typedef CComEnum > CComEnumConnections; public: CComConnectionPointBase(IConnectionPointContainer* pContainer, const IID* piid) { _ASSERTE(pContainer != NULL); _ASSERTE(piid != NULL); m_pContainer = pContainer; m_piid = piid; } //Connection point lifetimes are determined by the container STDMETHOD_(ULONG, AddRef)() {return m_pContainer->AddRef();} STDMETHOD_(ULONG, Release)(){return m_pContainer->Release();} STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { static const _ATL_INTMAP_ENTRY entries[] = {{&IID_IConnectionPoint, 0, _ATL_INTMAP_ENTRY::offset}}; return CComObjectRoot::InternalQueryInterface(this, entries, iid, ppvObject); } STDMETHOD(GetConnectionInterface)(IID* piid); STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC); protected: IConnectionPointContainer* m_pContainer; const IID* m_piid; }; template class CComConnectionPoint : public CComConnectionPointBase { typedef CComEnum > CComEnumConnections; public: CComConnectionPoint(IConnectionPointContainer* pContainer, const IID* piid) : CComConnectionPointBase(pContainer, piid) {m_sec.Init();} ~CComConnectionPoint() {m_sec.Term();} //Connection point lifetimes are determined by the container STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie); STDMETHOD(Unadvise)(DWORD dwCookie); STDMETHOD(EnumConnections)(IEnumConnections** ppEnum); protected: CComThreadModel::ObjectCriticalSection m_sec; CDV m_vec; friend class CComConnectionPointContainerImpl; }; template STDMETHODIMP CComConnectionPoint::Advise(IUnknown* pUnkSink, DWORD* pdwCookie) { IUnknown* p; HRESULT hRes = S_OK; if (pUnkSink == NULL || pdwCookie == NULL) return E_POINTER; m_sec.Lock(); if (SUCCEEDED(pUnkSink->QueryInterface(*m_piid, (void**)&p))) { *pdwCookie = (DWORD)p; hRes = m_vec.Add(p) ? S_OK : CONNECT_E_ADVISELIMIT; if (hRes != S_OK) { *pdwCookie = 0; p->Release(); } } else hRes = CONNECT_E_CANNOTCONNECT; m_sec.Unlock(); return hRes; } template STDMETHODIMP CComConnectionPoint::Unadvise(DWORD dwCookie) { m_sec.Lock(); HRESULT hRes = m_vec.Remove(dwCookie) ? S_OK : CONNECT_E_NOCONNECTION; IUnknown* p = (IUnknown*) dwCookie; m_sec.Unlock(); if (hRes == S_OK && p != NULL) p->Release(); return hRes; } template STDMETHODIMP CComConnectionPoint::EnumConnections( IEnumConnections** ppEnum) { CComEnumConnections* pEnum = new CComObject; m_sec.Lock(); CONNECTDATA* pcd = new CONNECTDATA[m_vec.end()-m_vec.begin()]; CONNECTDATA* pend = pcd; // Copy the valid CONNECTDATA's for (CONNECTDATA* p = m_vec.begin();ppUnk != NULL) { p->pUnk->AddRef(); *pend++ = *p; } } // don't copy the data, but transfer ownership to it pEnum->Init(pcd, pend, NULL, FALSE); pEnum->m_dwFlags |= CComEnumConnections::FlagCopy; m_sec.Unlock(); *ppEnum = pEnum; return S_OK; } template class CComConnectionPointContainerImpl : public IConnectionPointContainer { typedef CComEnum > CComEnumConnectionPoints; public: void FinalConstruct(); ~CComConnectionPointContainerImpl(); STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints** ppEnum); STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint** ppCP); protected: virtual const IID** _GetConnMap() = 0; CComConnectionPoint** m_ppCP; int m_nCP; }; template void CComConnectionPointContainerImpl::FinalConstruct() { const IID** ppiid= _GetConnMap(); m_nCP=0; while (*ppiid++ != NULL) m_nCP++; _ASSERTE(m_nCP > 0); m_ppCP = new CComConnectionPoint*[m_nCP]; CComConnectionPoint** ppCP = m_ppCP; ppiid= _GetConnMap(); for (int i=0;i(this, *ppiid); } } template CComConnectionPointContainerImpl::~CComConnectionPointContainerImpl() { _ASSERTE(m_nCP > 0); CComConnectionPoint** ppCP = m_ppCP; for (int i=0;i STDMETHODIMP CComConnectionPointContainerImpl::EnumConnectionPoints( IEnumConnectionPoints** ppEnum) { _ASSERTE(m_nCP > 0); CComEnumConnectionPoints* pEnum = new CComObject; // don't copy the data but do AddRef this object pEnum->Init((IConnectionPoint**)&m_ppCP[0], (IConnectionPoint**)&m_ppCP[m_nCP], this, FALSE); *ppEnum = pEnum; return S_OK; } template STDMETHODIMP CComConnectionPointContainerImpl::FindConnectionPoint( REFIID riid, IConnectionPoint** ppCP) { _ASSERTE(m_nCP > 0); if (ppCP == NULL) return E_POINTER; *ppCP = NULL; HRESULT hRes = CONNECT_E_NOCONNECTION; const IID** ppiid= _GetConnMap(); for (int i=0;iAddRef(); hRes = S_OK; break; } } return hRes; } #endif //!ATL_NOCONNPTS #pragma pack(pop) #endif // __ATLCOM_H__ /////////////////////////////////////////////////////////////////////////////