/* * ReactOS ATL * * Copyright 2009 Andrew Hill * Copyright 2013 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include // for GUID_NULL #include namespace ATL { template class CComEnum; #define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator > _ClassFactoryCreatorClass; #define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory) #define DECLARE_CLASSFACTORY_SINGLETON(obj) DECLARE_CLASSFACTORY_EX(ATL::CComClassFactorySingleton) class CComObjectRootBase { public: LONG m_dwRef; public: CComObjectRootBase() { m_dwRef = 0; } ~CComObjectRootBase() { } void SetVoid(void *) { } HRESULT _AtlFinalConstruct() { return S_OK; } HRESULT FinalConstruct() { return S_OK; } void InternalFinalConstructAddRef() { } void InternalFinalConstructRelease() { } void FinalRelease() { } static void WINAPI ObjectMain(bool) { } static const struct _ATL_CATMAP_ENTRY *GetCategoryMap() { return NULL; } static HRESULT WINAPI InternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObject) { return AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject); } }; template class CComObjectRootEx : public CComObjectRootBase { private: typename ThreadModel::AutoDeleteCriticalSection m_critsec; public: ~CComObjectRootEx() { } ULONG InternalAddRef() { ATLASSERT(m_dwRef >= 0); return ThreadModel::Increment(&m_dwRef); } ULONG InternalRelease() { ATLASSERT(m_dwRef > 0); return ThreadModel::Decrement(&m_dwRef); } void Lock() { m_critsec.Lock(); } void Unlock() { m_critsec.Unlock(); } HRESULT _AtlInitialConstruct() { return m_critsec.Init(); } }; template class CComObject : public Base { public: CComObject(void * = NULL) { _pAtlModule->Lock(); } virtual ~CComObject() { this->FinalRelease(); _pAtlModule->Unlock(); } STDMETHOD_(ULONG, AddRef)() { return this->InternalAddRef(); } STDMETHOD_(ULONG, Release)() { ULONG newRefCount; newRefCount = this->InternalRelease(); if (newRefCount == 0) delete this; return newRefCount; } STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject) { return this->_InternalQueryInterface(iid, ppvObject); } static HRESULT WINAPI CreateInstance(CComObject **pp) { CComObject *newInstance; HRESULT hResult; ATLASSERT(pp != NULL); if (pp == NULL) return E_POINTER; hResult = E_OUTOFMEMORY; newInstance = NULL; ATLTRY(newInstance = new CComObject()) if (newInstance != NULL) { newInstance->SetVoid(NULL); newInstance->InternalFinalConstructAddRef(); hResult = newInstance->_AtlInitialConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->FinalConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->_AtlFinalConstruct(); newInstance->InternalFinalConstructRelease(); if (hResult != S_OK) { delete newInstance; newInstance = NULL; } } *pp = newInstance; return hResult; } }; template class CComContainedObject : public Base { public: IUnknown* m_pUnkOuter; CComContainedObject(void * pv = NULL) : m_pUnkOuter(static_cast(pv)) { } STDMETHOD_(ULONG, AddRef)() { return m_pUnkOuter->AddRef(); } STDMETHOD_(ULONG, Release)() { return m_pUnkOuter->Release(); } STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject) { return m_pUnkOuter->QueryInterface(iid, ppvObject); } IUnknown* GetControllingUnknown() { return m_pUnkOuter; } }; template class CComAggObject : public contained { public: CComContainedObject m_contained; CComAggObject(void * pv = NULL) : m_contained(static_cast(pv)) { _pAtlModule->Lock(); } virtual ~CComAggObject() { this->FinalRelease(); _pAtlModule->Unlock(); } HRESULT FinalConstruct() { return m_contained.FinalConstruct(); } void FinalRelease() { m_contained.FinalRelease(); } STDMETHOD_(ULONG, AddRef)() { return this->InternalAddRef(); } STDMETHOD_(ULONG, Release)() { ULONG newRefCount; newRefCount = this->InternalRelease(); if (newRefCount == 0) delete this; return newRefCount; } STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject) { if (ppvObject == NULL) return E_POINTER; if (iid == IID_IUnknown) *ppvObject = reinterpret_cast(this); else return m_contained._InternalQueryInterface(iid, ppvObject); return S_OK; } static HRESULT WINAPI CreateInstance(IUnknown * punkOuter, CComAggObject **pp) { CComAggObject *newInstance; HRESULT hResult; ATLASSERT(pp != NULL); if (pp == NULL) return E_POINTER; hResult = E_OUTOFMEMORY; newInstance = NULL; ATLTRY(newInstance = new CComAggObject(punkOuter)) if (newInstance != NULL) { newInstance->SetVoid(NULL); newInstance->InternalFinalConstructAddRef(); hResult = newInstance->_AtlInitialConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->FinalConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->_AtlFinalConstruct(); newInstance->InternalFinalConstructRelease(); if (hResult != S_OK) { delete newInstance; newInstance = NULL; } } *pp = newInstance; return hResult; } }; template class CComPolyObject : public contained { public: CComContainedObject m_contained; CComPolyObject(void * pv = NULL) : m_contained(pv ? static_cast(pv) : this) { _pAtlModule->Lock(); } virtual ~CComPolyObject() { this->FinalRelease(); _pAtlModule->Unlock(); } HRESULT FinalConstruct() { return m_contained.FinalConstruct(); } void FinalRelease() { m_contained.FinalRelease(); } STDMETHOD_(ULONG, AddRef)() { return this->InternalAddRef(); } STDMETHOD_(ULONG, Release)() { ULONG newRefCount; newRefCount = this->InternalRelease(); if (newRefCount == 0) delete this; return newRefCount; } STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject) { if (ppvObject == NULL) return E_POINTER; if (iid == IID_IUnknown) *ppvObject = reinterpret_cast(this); else return m_contained._InternalQueryInterface(iid, ppvObject); return S_OK; } static HRESULT WINAPI CreateInstance(IUnknown * punkOuter, CComPolyObject **pp) { CComPolyObject *newInstance; HRESULT hResult; ATLASSERT(pp != NULL); if (pp == NULL) return E_POINTER; hResult = E_OUTOFMEMORY; newInstance = NULL; ATLTRY(newInstance = new CComPolyObject(punkOuter)) if (newInstance != NULL) { newInstance->SetVoid(NULL); newInstance->InternalFinalConstructAddRef(); hResult = newInstance->_AtlInitialConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->FinalConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->_AtlFinalConstruct(); newInstance->InternalFinalConstructRelease(); if (hResult != S_OK) { delete newInstance; newInstance = NULL; } } *pp = newInstance; return hResult; } }; template class CComFailCreator { public: static HRESULT WINAPI CreateInstance(void *, REFIID, LPVOID *ppv) { ATLASSERT(ppv != NULL); if (ppv == NULL) return E_POINTER; *ppv = NULL; return hResult; } }; template class CComCreator { public: static HRESULT WINAPI CreateInstance(void *pv, REFIID riid, LPVOID *ppv) { T1 *newInstance; HRESULT hResult; ATLASSERT(ppv != NULL); if (ppv == NULL) return E_POINTER; *ppv = NULL; hResult = E_OUTOFMEMORY; newInstance = NULL; ATLTRY(newInstance = new T1(pv)) if (newInstance != NULL) { newInstance->SetVoid(pv); newInstance->InternalFinalConstructAddRef(); hResult = newInstance->_AtlInitialConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->FinalConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->_AtlFinalConstruct(); newInstance->InternalFinalConstructRelease(); if (SUCCEEDED(hResult)) hResult = newInstance->QueryInterface(riid, ppv); if (FAILED(hResult)) { delete newInstance; newInstance = NULL; } } return hResult; } }; template class CComCreator2 { public: static HRESULT WINAPI CreateInstance(void *pv, REFIID riid, LPVOID *ppv) { ATLASSERT(ppv != NULL); if (pv == NULL) return T1::CreateInstance(NULL, riid, ppv); else return T2::CreateInstance(pv, riid, ppv); } }; template class CComObjectCached : public Base { public: CComObjectCached(void * = NULL) { } virtual ~CComObjectCached() { this->FinalRelease(); } STDMETHOD_(ULONG, AddRef)() { ULONG newRefCount; newRefCount = this->InternalAddRef(); if (newRefCount == 2) _pAtlModule->Lock(); return newRefCount; } STDMETHOD_(ULONG, Release)() { ULONG newRefCount; newRefCount = this->InternalRelease(); if (newRefCount == 0) delete this; else if (newRefCount == 1) _pAtlModule->Unlock(); return newRefCount; } STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject) { return this->_InternalQueryInterface(iid, ppvObject); } static HRESULT WINAPI CreateInstance(CComObjectCached **pp) { CComObjectCached *newInstance; HRESULT hResult; ATLASSERT(pp != NULL); if (pp == NULL) return E_POINTER; hResult = E_OUTOFMEMORY; newInstance = NULL; ATLTRY(newInstance = new CComObjectCached()) if (newInstance != NULL) { newInstance->SetVoid(NULL); newInstance->InternalFinalConstructAddRef(); hResult = newInstance->_AtlInitialConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->FinalConstruct(); if (SUCCEEDED(hResult)) hResult = newInstance->_AtlFinalConstruct(); newInstance->InternalFinalConstructRelease(); if (hResult != S_OK) { delete newInstance; newInstance = NULL; } } *pp = newInstance; return hResult; } }; #define BEGIN_COM_MAP(x) \ public: \ typedef x _ComMapClass; \ HRESULT _InternalQueryInterface(REFIID iid, void **ppvObject) \ { \ return this->InternalQueryInterface(this, _GetEntries(), iid, ppvObject); \ } \ const static ATL::_ATL_INTMAP_ENTRY *WINAPI _GetEntries() \ { \ static const ATL::_ATL_INTMAP_ENTRY _entries[] = { #define END_COM_MAP() \ {NULL, 0, 0} \ }; \ return _entries; \ } \ virtual ULONG STDMETHODCALLTYPE AddRef() = 0; \ virtual ULONG STDMETHODCALLTYPE Release() = 0; \ STDMETHOD(QueryInterface)(REFIID, void **) = 0; #define COM_INTERFACE_ENTRY_IID(iid, x) \ {&iid, offsetofclass(x, _ComMapClass), _ATL_SIMPLEMAPENTRY}, #define COM_INTERFACE_ENTRY(x) \ {&_ATL_IIDOF(x), \ offsetofclass(x, _ComMapClass), \ _ATL_SIMPLEMAPENTRY}, #define COM_INTERFACE_ENTRY2_IID(iid, x, x2) \ {&iid, \ reinterpret_cast(static_cast(static_cast(reinterpret_cast<_ComMapClass *>(_ATL_PACKING)))) - _ATL_PACKING, \ _ATL_SIMPLEMAPENTRY}, #define COM_INTERFACE_ENTRY_BREAK(x) \ {&_ATL_IIDOF(x), \ NULL, \ _Break}, // Break is a function that issues int 3. #define COM_INTERFACE_ENTRY_NOINTERFACE(x) \ {&_ATL_IIDOF(x), \ NULL, \ _NoInterface}, // NoInterface returns E_NOINTERFACE. #define COM_INTERFACE_ENTRY_FUNC(iid, dw, func) \ {&iid, \ dw, \ func}, #define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func) \ {NULL, \ dw, \ func}, #define COM_INTERFACE_ENTRY_CHAIN(classname) \ {NULL, \ reinterpret_cast(&_CComChainData::data), \ _Chain}, #define DECLARE_NO_REGISTRY()\ static HRESULT WINAPI UpdateRegistry(BOOL /*bRegister*/) \ { \ return S_OK; \ } #define DECLARE_REGISTRY_RESOURCEID(x) \ static HRESULT WINAPI UpdateRegistry(BOOL bRegister) \ { \ return ATL::_pAtlModule->UpdateRegistryFromResource(x, bRegister); \ } #define DECLARE_NOT_AGGREGATABLE(x) \ public: \ typedef ATL::CComCreator2 >, ATL::CComFailCreator > _CreatorClass; #define DECLARE_AGGREGATABLE(x) \ public: \ typedef ATL::CComCreator2 >, ATL::CComCreator > > _CreatorClass; #define DECLARE_ONLY_AGGREGATABLE(x) \ public: \ typedef ATL::CComCreator2, ATL::CComCreator > > _CreatorClass; #define DECLARE_POLY_AGGREGATABLE(x) \ public: \ typedef ATL::CComCreator > _CreatorClass; #define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) \ {&iid, \ (DWORD_PTR)offsetof(_ComMapClass, punk), \ _Delegate}, #define DECLARE_GET_CONTROLLING_UNKNOWN() \ public: \ virtual IUnknown *GetControllingUnknown() \ { \ return GetUnknown(); \ } #define DECLARE_PROTECT_FINAL_CONSTRUCT() \ void InternalFinalConstructAddRef() \ { \ InternalAddRef(); \ } \ void InternalFinalConstructRelease() \ { \ InternalRelease(); \ } #define BEGIN_OBJECT_MAP(x) static ATL::_ATL_OBJMAP_ENTRY x[] = { #define END_OBJECT_MAP() {NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL}}; #define OBJECT_ENTRY(clsid, class) \ { \ &clsid, \ class::UpdateRegistry, \ class::_ClassFactoryCreatorClass::CreateInstance, \ class::_CreatorClass::CreateInstance, \ NULL, \ 0, \ class::GetObjectDescription, \ class::GetCategoryMap, \ class::ObjectMain }, class CComClassFactory : public IClassFactory, public CComObjectRootEx { public: _ATL_CREATORFUNC *m_pfnCreateInstance; virtual ~CComClassFactory() { } public: STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj) { HRESULT hResult; ATLASSERT(m_pfnCreateInstance != NULL); if (ppvObj == NULL) return E_POINTER; *ppvObj = NULL; if (pUnkOuter != NULL && InlineIsEqualUnknown(riid) == FALSE) hResult = CLASS_E_NOAGGREGATION; else hResult = m_pfnCreateInstance(pUnkOuter, riid, ppvObj); return hResult; } STDMETHOD(LockServer)(BOOL fLock) { if (fLock) _pAtlModule->Lock(); else _pAtlModule->Unlock(); return S_OK; } void SetVoid(void *pv) { m_pfnCreateInstance = (_ATL_CREATORFUNC *)pv; } BEGIN_COM_MAP(CComClassFactory) COM_INTERFACE_ENTRY_IID(IID_IClassFactory, IClassFactory) END_COM_MAP() }; template class CComClassFactorySingleton : public CComClassFactory { public: HRESULT m_hrCreate; IUnknown *m_spObj; public: CComClassFactorySingleton() : m_hrCreate(S_OK), m_spObj(NULL) { } STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj) { HRESULT hResult; if (ppvObj == NULL) return E_POINTER; *ppvObj = NULL; if (pUnkOuter != NULL) hResult = CLASS_E_NOAGGREGATION; else if (m_hrCreate == S_OK && m_spObj == NULL) { _SEH2_TRY { Lock(); if (m_hrCreate == S_OK && m_spObj == NULL) { CComObjectCached *pObj; m_hrCreate = CComObjectCached::CreateInstance(&pObj); if (SUCCEEDED(m_hrCreate)) { m_hrCreate = pObj->QueryInterface(IID_IUnknown, reinterpret_cast(&m_spObj)); if (FAILED(m_hrCreate)) delete pObj; } } } _SEH2_FINALLY { Unlock(); } _SEH2_END; } if (m_hrCreate == S_OK) hResult = m_spObj->QueryInterface(riid, ppvObj); else hResult = m_hrCreate; return hResult; } }; template class CComCoClass { public: DECLARE_CLASSFACTORY() static LPCTSTR WINAPI GetObjectDescription() { return NULL; } }; template class _Copy { public: static HRESULT copy(T *pTo, const T *pFrom) { memcpy(pTo, pFrom, sizeof(T)); return S_OK; } static void init(T *) { } static void destroy(T *) { } }; template<> class _Copy { public: static HRESULT copy(CONNECTDATA *pTo, const CONNECTDATA *pFrom) { *pTo = *pFrom; if (pTo->pUnk) pTo->pUnk->AddRef(); return S_OK; } static void init(CONNECTDATA *) { } static void destroy(CONNECTDATA *p) { if (p->pUnk) p->pUnk->Release(); } }; template class _CopyInterface { public: static HRESULT copy(T **pTo, T **pFrom) { *pTo = *pFrom; if (*pTo) (*pTo)->AddRef(); return S_OK; } static void init(T **) { } static void destroy(T **p) { if (*p) (*p)->Release(); } }; enum CComEnumFlags { AtlFlagNoCopy = 0, AtlFlagTakeOwnership = 2, // BitOwn AtlFlagCopy = 3 // BitOwn | BitCopy }; template class CComEnumImpl : public Base { private: typedef CComObject > enumeratorClass; public: CComPtr m_spUnk; DWORD m_dwFlags; T *m_begin; T *m_end; T *m_iter; public: CComEnumImpl() { m_dwFlags = 0; m_begin = NULL; m_end = NULL; m_iter = NULL; } virtual ~CComEnumImpl() { T *x; if ((m_dwFlags & BitOwn) != 0) { for (x = m_begin; x != m_end; x++) Copy::destroy(x); delete [] m_begin; } } HRESULT Init(T *begin, T *end, IUnknown *pUnk, CComEnumFlags flags = AtlFlagNoCopy) { T *newBuffer; T *sourcePtr; T *destPtr; T *cleanupPtr; HRESULT hResult; if (flags == AtlFlagCopy) { ATLTRY(newBuffer = new T[end - begin]) if (newBuffer == NULL) return E_OUTOFMEMORY; destPtr = newBuffer; for (sourcePtr = begin; sourcePtr != end; sourcePtr++) { Copy::init(destPtr); hResult = Copy::copy(destPtr, sourcePtr); if (FAILED(hResult)) { cleanupPtr = m_begin; while (cleanupPtr < destPtr) Copy::destroy(cleanupPtr++); delete [] newBuffer; return hResult; } destPtr++; } m_begin = newBuffer; m_end = m_begin + (end - begin); } else { m_begin = begin; m_end = end; } m_spUnk = pUnk; m_dwFlags = flags; m_iter = m_begin; return S_OK; } STDMETHOD(Next)(ULONG celt, T *rgelt, ULONG *pceltFetched) { ULONG numAvailable; ULONG numToFetch; T *rgeltTemp; HRESULT hResult; if (pceltFetched != NULL) *pceltFetched = 0; if (celt == 0) return E_INVALIDARG; if (rgelt == NULL || (celt != 1 && pceltFetched == NULL)) return E_POINTER; if (m_begin == NULL || m_end == NULL || m_iter == NULL) return E_FAIL; numAvailable = static_cast(m_end - m_iter); if (celt < numAvailable) numToFetch = celt; else numToFetch = numAvailable; if (pceltFetched != NULL) *pceltFetched = numToFetch; rgeltTemp = rgelt; while (numToFetch != 0) { hResult = Copy::copy(rgeltTemp, m_iter); if (FAILED(hResult)) { while (rgelt < rgeltTemp) Copy::destroy(rgelt++); if (pceltFetched != NULL) *pceltFetched = 0; return hResult; } rgeltTemp++; m_iter++; numToFetch--; } if (numAvailable < celt) return S_FALSE; return S_OK; } STDMETHOD(Skip)(ULONG celt) { ULONG numAvailable; ULONG numToSkip; if (celt == 0) return E_INVALIDARG; numAvailable = static_cast(m_end - m_iter); if (celt < numAvailable) numToSkip = celt; else numToSkip = numAvailable; m_iter += numToSkip; if (numAvailable < celt) return S_FALSE; return S_OK; } STDMETHOD(Reset)() { m_iter = m_begin; return S_OK; } STDMETHOD(Clone)(Base **ppEnum) { enumeratorClass *newInstance; HRESULT hResult; hResult = E_POINTER; if (ppEnum != NULL) { *ppEnum = NULL; hResult = enumeratorClass::CreateInstance(&newInstance); if (SUCCEEDED(hResult)) { hResult = newInstance->Init(m_begin, m_end, (m_dwFlags & BitOwn) ? this : m_spUnk); if (SUCCEEDED(hResult)) { newInstance->m_iter = m_iter; hResult = newInstance->_InternalQueryInterface(*piid, (void **)ppEnum); } if (FAILED(hResult)) delete newInstance; } } return hResult; } protected: enum FlagBits { BitCopy = 1, BitOwn = 2 }; }; template class CComEnum : public CComEnumImpl, public CComObjectRootEx { public: typedef CComEnum _CComEnum; typedef CComEnumImpl _CComEnumBase; BEGIN_COM_MAP(_CComEnum) COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase) END_COM_MAP() }; #ifndef _DEFAULT_VECTORLENGTH #define _DEFAULT_VECTORLENGTH 4 #endif class CComDynamicUnkArray { public: int m_nSize; IUnknown **m_ppUnk; public: CComDynamicUnkArray() { m_nSize = 0; m_ppUnk = NULL; } ~CComDynamicUnkArray() { free(m_ppUnk); } IUnknown **begin() { return m_ppUnk; } IUnknown **end() { return &m_ppUnk[m_nSize]; } IUnknown *GetAt(int nIndex) { ATLASSERT(nIndex >= 0 && nIndex < m_nSize); if (nIndex >= 0 && nIndex < m_nSize) return m_ppUnk[nIndex]; else return NULL; } IUnknown *WINAPI GetUnknown(DWORD dwCookie) { ATLASSERT(dwCookie != 0 && dwCookie <= static_cast(m_nSize)); if (dwCookie != 0 && dwCookie <= static_cast(m_nSize)) return GetAt(dwCookie - 1); else return NULL; } DWORD WINAPI GetCookie(IUnknown **ppFind) { IUnknown **x; DWORD curCookie; ATLASSERT(ppFind != NULL && *ppFind != NULL); if (ppFind != NULL && *ppFind != NULL) { curCookie = 1; for (x = begin(); x < end(); x++) { if (*x == *ppFind) return curCookie; curCookie++; } } return 0; } DWORD Add(IUnknown *pUnk) { IUnknown **x; IUnknown **newArray; int newSize; DWORD curCookie; ATLASSERT(pUnk != NULL); if (m_nSize == 0) { newSize = _DEFAULT_VECTORLENGTH * sizeof(IUnknown *); ATLTRY(newArray = reinterpret_cast(malloc(newSize))); if (newArray == NULL) return 0; memset(newArray, 0, newSize); m_ppUnk = newArray; m_nSize = _DEFAULT_VECTORLENGTH; } curCookie = 1; for (x = begin(); x < end(); x++) { if (*x == NULL) { *x = pUnk; return curCookie; } curCookie++; } newSize = m_nSize * 2; newArray = reinterpret_cast(realloc(m_ppUnk, newSize * sizeof(IUnknown *))); if (newArray == NULL) return 0; m_ppUnk = newArray; memset(&m_ppUnk[m_nSize], 0, (newSize - m_nSize) * sizeof(IUnknown *)); curCookie = m_nSize + 1; m_nSize = newSize; m_ppUnk[curCookie - 1] = pUnk; return curCookie; } BOOL Remove(DWORD dwCookie) { DWORD index; index = dwCookie - 1; ATLASSERT(index < dwCookie && index < static_cast(m_nSize)); if (index < dwCookie && index < static_cast(m_nSize) && m_ppUnk[index] != NULL) { m_ppUnk[index] = NULL; return TRUE; } return FALSE; } private: CComDynamicUnkArray &operator = (const CComDynamicUnkArray &) { return *this; } CComDynamicUnkArray(const CComDynamicUnkArray &) { } }; struct _ATL_CONNMAP_ENTRY { DWORD_PTR dwOffset; }; template class _ICPLocator { public: STDMETHOD(_LocCPQueryInterface)(REFIID riid, void **ppvObject) = 0; virtual ULONG STDMETHODCALLTYPE AddRef() = 0; virtual ULONG STDMETHODCALLTYPE Release() = 0; }; template class IConnectionPointImpl : public _ICPLocator { typedef CComEnum > CComEnumConnections; public: CDV m_vec; public: ~IConnectionPointImpl() { IUnknown **x; for (x = m_vec.begin(); x < m_vec.end(); x++) if (*x != NULL) (*x)->Release(); } STDMETHOD(_LocCPQueryInterface)(REFIID riid, void **ppvObject) { IConnectionPointImpl *pThis; pThis = reinterpret_cast*>(this); ATLASSERT(ppvObject != NULL); if (ppvObject == NULL) return E_POINTER; if (InlineIsEqualGUID(riid, IID_IConnectionPoint) || InlineIsEqualUnknown(riid)) { *ppvObject = this; pThis->AddRef(); return S_OK; } else { *ppvObject = NULL; return E_NOINTERFACE; } } STDMETHOD(GetConnectionInterface)(IID *piid2) { if (piid2 == NULL) return E_POINTER; *piid2 = *piid; return S_OK; } STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer **ppCPC) { T *pThis; pThis = static_cast(this); return pThis->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast(ppCPC)); } STDMETHOD(Advise)(IUnknown *pUnkSink, DWORD *pdwCookie) { IUnknown *adviseTarget; IID interfaceID; HRESULT hResult; if (pdwCookie != NULL) *pdwCookie = 0; if (pUnkSink == NULL || pdwCookie == NULL) return E_POINTER; GetConnectionInterface(&interfaceID); // can't fail hResult = pUnkSink->QueryInterface(interfaceID, reinterpret_cast(&adviseTarget)); if (SUCCEEDED(hResult)) { *pdwCookie = m_vec.Add(adviseTarget); if (*pdwCookie != 0) hResult = S_OK; else { adviseTarget->Release(); hResult = CONNECT_E_ADVISELIMIT; } } else if (hResult == E_NOINTERFACE) hResult = CONNECT_E_CANNOTCONNECT; return hResult; } STDMETHOD(Unadvise)(DWORD dwCookie) { IUnknown *adviseTarget; HRESULT hResult; adviseTarget = m_vec.GetUnknown(dwCookie); if (m_vec.Remove(dwCookie)) { if (adviseTarget != NULL) adviseTarget->Release(); hResult = S_OK; } else hResult = CONNECT_E_NOCONNECTION; return hResult; } STDMETHOD(EnumConnections)(IEnumConnections **ppEnum) { CComObject *newEnumerator; CONNECTDATA *itemBuffer; CONNECTDATA *itemBufferEnd; IUnknown **x; HRESULT hResult; ATLASSERT(ppEnum != NULL); if (ppEnum == NULL) return E_POINTER; *ppEnum = NULL; ATLTRY(itemBuffer = new CONNECTDATA[m_vec.end() - m_vec.begin()]) if (itemBuffer == NULL) return E_OUTOFMEMORY; itemBufferEnd = itemBuffer; for (x = m_vec.begin(); x < m_vec.end(); x++) { if (*x != NULL) { (*x)->AddRef(); itemBufferEnd->pUnk = *x; itemBufferEnd->dwCookie = m_vec.GetCookie(x); itemBufferEnd++; } } ATLTRY(newEnumerator = new CComObject) if (newEnumerator == NULL) return E_OUTOFMEMORY; newEnumerator->Init(itemBuffer, itemBufferEnd, NULL, AtlFlagTakeOwnership); // can't fail hResult = newEnumerator->_InternalQueryInterface(IID_IEnumConnections, (void **)ppEnum); if (FAILED(hResult)) delete newEnumerator; return hResult; } }; template class IConnectionPointContainerImpl : public IConnectionPointContainer { typedef const _ATL_CONNMAP_ENTRY * (*handlerFunctionType)(int *); typedef CComEnum > CComEnumConnectionPoints; public: STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints **ppEnum) { const _ATL_CONNMAP_ENTRY *entryPtr; int connectionPointCount; IConnectionPoint **itemBuffer; int destIndex; handlerFunctionType handlerFunction; CComEnumConnectionPoints *newEnumerator; HRESULT hResult; ATLASSERT(ppEnum != NULL); if (ppEnum == NULL) return E_POINTER; *ppEnum = NULL; entryPtr = T::GetConnMap(&connectionPointCount); ATLTRY(itemBuffer = new IConnectionPoint * [connectionPointCount]) if (itemBuffer == NULL) return E_OUTOFMEMORY; destIndex = 0; while (entryPtr->dwOffset != static_cast(-1)) { if (entryPtr->dwOffset == static_cast(-2)) { entryPtr++; handlerFunction = reinterpret_cast(entryPtr->dwOffset); entryPtr = handlerFunction(NULL); } else { itemBuffer[destIndex++] = reinterpret_cast((char *)this + entryPtr->dwOffset); entryPtr++; } } ATLTRY(newEnumerator = new CComObject) if (newEnumerator == NULL) { delete [] itemBuffer; return E_OUTOFMEMORY; } newEnumerator->Init(&itemBuffer[0], &itemBuffer[destIndex], NULL, AtlFlagTakeOwnership); // can't fail hResult = newEnumerator->QueryInterface(IID_IEnumConnectionPoints, (void**)ppEnum); if (FAILED(hResult)) delete newEnumerator; return hResult; } STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint **ppCP) { IID interfaceID; const _ATL_CONNMAP_ENTRY *entryPtr; handlerFunctionType handlerFunction; IConnectionPoint *connectionPoint; HRESULT hResult; if (ppCP == NULL) return E_POINTER; *ppCP = NULL; hResult = CONNECT_E_NOCONNECTION; entryPtr = T::GetConnMap(NULL); while (entryPtr->dwOffset != static_cast(-1)) { if (entryPtr->dwOffset == static_cast(-2)) { entryPtr++; handlerFunction = reinterpret_cast(entryPtr->dwOffset); entryPtr = handlerFunction(NULL); } else { connectionPoint = reinterpret_cast(reinterpret_cast(this) + entryPtr->dwOffset); if (SUCCEEDED(connectionPoint->GetConnectionInterface(&interfaceID)) && InlineIsEqualGUID(riid, interfaceID)) { *ppCP = connectionPoint; connectionPoint->AddRef(); hResult = S_OK; break; } entryPtr++; } } return hResult; } }; #define BEGIN_CONNECTION_POINT_MAP(x) \ typedef x _atl_conn_classtype; \ static const ATL::_ATL_CONNMAP_ENTRY *GetConnMap(int *pnEntries) { \ static const ATL::_ATL_CONNMAP_ENTRY _entries[] = { #define END_CONNECTION_POINT_MAP() \ {(DWORD_PTR)-1} }; \ if (pnEntries) \ *pnEntries = sizeof(_entries) / sizeof(ATL::_ATL_CONNMAP_ENTRY) - 1; \ return _entries;} #define CONNECTION_POINT_ENTRY(iid) \ {offsetofclass(ATL::_ICPLocator<&iid>, _atl_conn_classtype) - \ offsetofclass(ATL::IConnectionPointContainerImpl<_atl_conn_classtype>, _atl_conn_classtype)}, /* TODO: - IDispatchImpl contains a static member of type CComTypeInfoHolder that manages the type information for the dual interface. If you have multiple objects that implement the same dual interface, only one instance of CComTypeInfoHolder is used. - By default, the IDispatchImpl class looks up the type information for T in the registry. To implement an unregistered interface, you can use the IDispatchImpl class without accessing the registry by using a predefined version number. If you create an IDispatchImpl object that has 0xFFFF as the value for wMajor and 0xFFFF as the value for wMinor, the IDispatchImpl class retrieves the type library from the .dll file instead of the registry. */ template class IDispatchImpl : public T { private: CComPtr m_pTypeInfo; STDMETHOD(EnsureTILoaded)(LCID lcid) { HRESULT hr = S_OK; if (m_pTypeInfo != NULL) return hr; if (IsEqualCLSID(CLSID_NULL, *plibid)) OutputDebugStringA("IDispatchImpl: plibid is CLSID_NULL!\r\n"); // Should we assert here? if (wMajor == 0xffff && wMinor == 0xffff) OutputDebugStringA("IDispatchImpl: not fully implemented, missing functionality to load TLB from file!\r\n"); CComPtr spTypeLib; hr = LoadRegTypeLib(*plibid, wMajor, wMinor, lcid, &spTypeLib); if (SUCCEEDED(hr)) { hr = spTypeLib->GetTypeInfoOfGuid(*piid, &m_pTypeInfo); } return hr; } public: IDispatchImpl() { } // *** IDispatch methods *** STDMETHOD(GetTypeInfoCount)(UINT *pctinfo) { if (pctinfo == NULL) return E_POINTER; *pctinfo = 1; return S_OK; } STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { if (iTInfo != 0) return DISP_E_BADINDEX; if (ppTInfo == NULL) return E_POINTER; HRESULT hr = EnsureTILoaded(lcid); *ppTInfo = m_pTypeInfo; if (*ppTInfo) (*ppTInfo)->AddRef(); return hr; } STDMETHOD(GetIDsOfNames)(REFIID /*riid*/, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { HRESULT hr = EnsureTILoaded(lcid); if (SUCCEEDED(hr)) hr = m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId); return hr; } STDMETHOD(Invoke)(DISPID dispIdMember, REFIID /*riid*/, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { HRESULT hr = EnsureTILoaded(lcid); if (SUCCEEDED(hr)) hr = m_pTypeInfo->Invoke(this, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); return hr; } }; }; // namespace ATL