// comvector.h: Wrapper around a single-dim SAFEARRAY and a lock on a SAFEARRAY ///////////////////////////////////////////////////////////////////////////// // Copyright (c) 1999-2001, Chris Sells. All rights reserved. No warranties extended. // Comments to csells@sellsbrothers.com. ///////////////////////////////////////////////////////////////////////////// // CComVector is a wrapper around a single-dim SAFEARRAY. // CComVectorData is a wrapper around a lock on a SAFEARRAY, acquired via // SafeArrayAccessData. // // By splitting the two concepts into two classes, each can be managed // separately, therefore avoiding the overhead and inconvenience of // SafeArrayGetElement and SafeArrayPutElement. // // By specializing on one dimension, the language mapping is easy. // // Also added CComVectorItf, a SAFEARRAY of VARIANTs holding some interface. ///////////////////////////////////////////////////////////////////////////// // History: // 1/10/01: Fixed an ASSERT bug in CComVector::Create. Thanks to // Phil Atkin [phil.atkin@synoptics.co.uk]. // 1/3/01: Fixed a compile bug with the suggested usage pointed // out by Claus Michelsen [clausmic@btinternet.com]. // Also removed the restriction on operator&, because SAs are // passed [in, out] all the time. // 10/25/00: Added support for VARIANTs holding SAFEARRAYs and // auto-coercing SAFEARRAYs of VARIANTS to a specific itf. // 9/26/00: Assert on VT_BYREF, VT_ARRAY, VT_NULL and VT_EMPTY in Create, // as per the docs (as pointed out by Brian Moelk // [bmoelk@answerlogic.com]). // 7/29/00: Added RawData method to get access to raw SA data. // 5/13/99: Initial release. ///////////////////////////////////////////////////////////////////////////// // Usage: /* STDMETHODIMP CFoo::UseSafeArray(SAFEARRAY** ppsa) // [in] { CComVectorData rg(*ppsa); if( !rg ) return E_UNEXPECTED; for( int i = 0; i < rg.Length(); ++i ) { TCHAR sz[32]; wsprintf(sz, __T("rg[%d]= %S\n"), i, (rg[i] ? rg[i] : OLESTR(""))); OutputDebugString(sz); } // Assume data member: 'CComVector m_rg;' m_rg = *ppsa; // Cache a copy return S_OK; } STDMETHODIMP CFoo::GetSafeArray(SAFEARRAY** ppsa) // [in, out] { CComVector v(100); if( !v ) return E_OUTOFMEMORY; CComVectorData rg(v); if( !rg ) return E_UNEXPECTED; for( int i = 0; i < 100; ++i ) { char sz[16]; rg[i] = A2BSTR(itoa(i, sz, 10)); } return v.DetachTo(ppsa); } */ #pragma once #ifndef INC_COMVECTOR #define INC_COMVECTOR #include #ifndef HR #define HR(_ex) { HRESULT _hr = _ex; if( FAILED(_hr) ) return _hr; } #endif ///////////////////////////////////////////////////////////////////////////// // CComVector wraps a SAFEARRAY* class CComVectorBase { public: CComVectorBase(const SAFEARRAY* psa) : m_psa(0) { if( psa ) Copy(psa); } CComVectorBase(const VARIANT& var) : m_psa(0) { if( var.vt | VT_ARRAY ) Copy(var.parray); } CComVectorBase(const CComVectorBase& v) : m_psa(0) { if( v.m_psa ) Copy(v.m_psa); } ~CComVectorBase() { Destroy(); } HRESULT Copy(const SAFEARRAY* psa) { Destroy(); if( psa ) { _ASSERTE(SafeArrayGetDim(const_cast(psa)) == 1); HR(SafeArrayCopy(const_cast(psa), &m_psa)); } return S_OK; } HRESULT Destroy() { if( !m_psa ) return S_OK; HRESULT hr = SafeArrayDestroy(m_psa); m_psa = 0; return hr; } void Attach(SAFEARRAY* psa) { Destroy(); m_psa = psa; } SAFEARRAY* Detach() { SAFEARRAY* psa = m_psa; m_psa = 0; return psa; } HRESULT DetachTo(/*[in, out]*/ SAFEARRAY** ppsa) { if( !ppsa ) HR(E_POINTER); // If the [in] array is already created, destroy it if( *ppsa ) HR(SafeArrayDestroy(*ppsa)); *ppsa = m_psa; m_psa = 0; return S_OK; } HRESULT CopyTo(/*[in, out]*/ SAFEARRAY** ppsa) { if( !ppsa ) HR(E_POINTER); // If the [in] array is already created, destroy it if( *ppsa ) HR(SafeArrayDestroy(*ppsa)); HR(SafeArrayCopy(m_psa, ppsa)); return S_OK; } operator SAFEARRAY*() { return m_psa; } SAFEARRAY** operator&() { // NOTE: This isn't reasonable, as SAs are passed [in, out] all the time //_ASSERTE(!m_psa); return &m_psa; } long Length() { return Length(m_psa); } public: static long Length(const SAFEARRAY* psa) { if( !psa ) return 0; _ASSERTE(SafeArrayGetDim(const_cast(psa)) == 1); long ub; SafeArrayGetUBound(const_cast(psa), 1, &ub); long lb; SafeArrayGetLBound(const_cast(psa), 1, &lb); return ub - lb + 1; } protected: template VARTYPE VarType(T*); template<> VARTYPE VarType(LONG*) { return VT_I4; } template<> VARTYPE VarType(BYTE*) { return VT_UI1; } template<> VARTYPE VarType(SHORT*) { return VT_I2; } template<> VARTYPE VarType(FLOAT*) { return VT_R4; } template<> VARTYPE VarType(DOUBLE*) { return VT_R8; } template<> VARTYPE VarType(VARIANT_BOOL*) { return VT_BOOL; } template<> VARTYPE VarType(SCODE*) { return VT_ERROR; } template<> VARTYPE VarType(CY*) { return VT_CY; } template<> VARTYPE VarType(DATE*) { return VT_DATE; } template<> VARTYPE VarType(BSTR*) { return VT_BSTR; } template<> VARTYPE VarType(IUnknown **) { return VT_UNKNOWN; } template<> VARTYPE VarType(IDispatch **) { return VT_DISPATCH; } template<> VARTYPE VarType(SAFEARRAY **) { return VT_ARRAY; } template<> VARTYPE VarType(CHAR*) { return VT_I1; } template<> VARTYPE VarType(USHORT*) { return VT_UI2; } template<> VARTYPE VarType(ULONG*) { return VT_UI4; } template<> VARTYPE VarType(INT*) { return VT_INT; } template<> VARTYPE VarType(UINT*) { return VT_UINT; } template<> VARTYPE VarType(VARIANT*) { return VT_VARIANT; } template<> VARTYPE VarType(BYTE **) { return VT_BYREF|VT_UI1; } template<> VARTYPE VarType(SHORT **) { return VT_BYREF|VT_I2; } template<> VARTYPE VarType(LONG **) { return VT_BYREF|VT_I4; } template<> VARTYPE VarType(FLOAT **) { return VT_BYREF|VT_R4; } template<> VARTYPE VarType(DOUBLE **) { return VT_BYREF|VT_R8; } template<> VARTYPE VarType(VARIANT_BOOL **) { return VT_BYREF|VT_BOOL; } template<> VARTYPE VarType(SCODE **) { return VT_BYREF|VT_ERROR; } template<> VARTYPE VarType(CY **) { return VT_BYREF|VT_CY; } template<> VARTYPE VarType(DATE **) { return VT_BYREF|VT_DATE; } template<> VARTYPE VarType(BSTR **) { return VT_BYREF|VT_BSTR; } template<> VARTYPE VarType(IUnknown ***) { return VT_BYREF|VT_UNKNOWN; } template<> VARTYPE VarType(IDispatch ***) { return VT_BYREF|VT_DISPATCH; } template<> VARTYPE VarType(SAFEARRAY ***) { return VT_BYREF|VT_ARRAY; } template<> VARTYPE VarType(VARIANT **) { return VT_BYREF|VT_VARIANT; } template<> VARTYPE VarType(PVOID*) { return VT_BYREF; } template<> VARTYPE VarType(DECIMAL **) { return VT_BYREF|VT_DECIMAL; } template<> VARTYPE VarType(CHAR **) { return VT_BYREF|VT_I1; } template<> VARTYPE VarType(USHORT **) { return VT_BYREF|VT_UI2; } template<> VARTYPE VarType(ULONG **) { return VT_BYREF|VT_UI4; } template<> VARTYPE VarType(INT **) { return VT_BYREF|VT_INT; } template<> VARTYPE VarType(UINT **) { return VT_BYREF|VT_UINT; } template<> VARTYPE VarType(VARIANT **) { return VT_BYREF|VT_VARIANT; } protected: SAFEARRAY* m_psa; }; template class CComVector : public CComVectorBase { public: CComVector(SAFEARRAY* psa = 0) : CComVectorBase(psa) { } CComVector(const CComVector& v) : CComVectorBase(v) { } CComVector(long celt, long nLowerBound = 0, bool bZeroMemory = true) : CComVectorBase(0) { Create(celt, nLowerBound, bZeroMemory); } // TODO: Hook up Copy policy classes /* CComVector(const T* rg, long celt) : m_psa(0) { Copy(rg, celt); } */ CComVector& operator=(const SAFEARRAY* psa) { if( psa != m_psa ) Copy(psa); return *this; } CComVector& operator=(const CComVector& v) { if( &v != this ) Copy(v.m_psa); return *this; } HRESULT Create(long celt, long nLowerBound = 0, bool bZeroMemory = true) { #ifdef _DEBUG VARTYPE vt = VarType((T*)0); _ASSERTE(((vt & VT_BYREF) == 0) && "Can't create a SA using VT_BYREF"); _ASSERTE(((vt & VT_ARRAY) == 0) && "Can't create a SA using VT_ARRAY"); _ASSERTE(((vt & VT_TYPEMASK) != VT_EMPTY) && "Can't create a SA using VT_EMPTY"); _ASSERTE(((vt & VT_TYPEMASK) != VT_NULL) && "Can't create a SA using VT_NULL"); #endif SAFEARRAYBOUND sab = { celt, nLowerBound }; m_psa = SafeArrayCreate(VarType((T*)0), 1, &sab); if( !m_psa ) HR(E_OUTOFMEMORY); if( bZeroMemory ) { T* rg = 0; HR(SafeArrayAccessData(m_psa, (void**)&rg)); ZeroMemory(rg, sizeof(T) * celt); SafeArrayUnaccessData(m_psa); } return S_OK; } T* RawData() { _ASSERTE(m_psa); return reinterpret_cast(m_psa->pvData); } // For some reason, VC6 won't bind to the base class's // implementation, so we do it ourselves HRESULT DetachTo(/*[in, out]*/ SAFEARRAY** ppsa) { return CComVectorBase::DetachTo(ppsa); } // For some reason, VC6 won't bind to the base class's // implementation, so we do it ourselves HRESULT CopyTo(/*[in, out]*/ SAFEARRAY** ppsa) { return CComVectorBase::CopyTo(ppsa); } HRESULT DetachTo(/*[out, retval]*/ VARIANT* pvar) { if( !pvar ) return E_POINTER; pvar->vt = VT_ARRAY | VarType((T*)0); pvar->parray = 0; return CComVectorBase::DetachTo(&pvar->parray); } HRESULT CopyTo(/*[out, retval]*/ VARIANT* pvar) { if( !pvar ) return E_POINTER; pvar->vt = VT_ARRAY | VarType((T*)0); pvar->parray = 0; return CComVectorBase::CopyTo(&pvar->parray); } }; ///////////////////////////////////////////////////////////////////////////// // CComVectorData represents a lock on SAFEARRAY data class CComVectorDataBase { public: CComVectorDataBase(SAFEARRAY* psa) : m_psa(0), m_pv(0), m_celt(0) { if( psa ) AccessData(psa); } CComVectorDataBase(VARIANT& var) : m_psa(0), m_pv(0), m_celt(0) { AccessData(var); } ~CComVectorDataBase() { UnaccessData(); } HRESULT AccessData(SAFEARRAY* psa) { if( !psa || (SafeArrayGetDim(psa) != 1) ) return E_INVALIDARG; UnaccessData(); HR(SafeArrayAccessData(psa, &m_pv)); m_psa = psa; m_celt = CComVectorBase::Length(m_psa); return S_OK; } HRESULT AccessData(VARIANT& var) { if( !(var.vt & VT_ARRAY) ) return E_INVALIDARG; return AccessData(var.parray); } HRESULT UnaccessData() { if( m_psa && m_pv ) HR(SafeArrayUnaccessData(m_psa)); m_psa = 0; m_pv = 0; m_celt = 0; return S_OK; } long Length() { return m_celt; } operator bool() { return (m_pv ? true : false); } protected: SAFEARRAY* m_psa; void* m_pv; long m_celt; }; template class CComVectorData : public CComVectorDataBase { public: CComVectorData(SAFEARRAY* psa = 0) : CComVectorDataBase(psa) { } CComVectorData(VARIANT& var) : CComVectorDataBase(var) { } T& operator[](long n) { #ifdef _DEBUG if( n < 0 || n >= m_celt ) { _ASSERTE(false && "Accessing data out of bounds"); } #endif return reinterpret_cast(m_pv)[n]; } /* This conflicts with operator[] operator T*() { return reinterpret_cast(m_pv); } */ }; // A SAFEARRAY of VARIANTs holding some interface template class CComVectorItf : public CComVectorData { public: CComVectorItf(SAFEARRAY* psa = 0) : CComVectorData(psa) { } CComVectorItf(VARIANT& var) : CComVectorData(var) { } HRESULT QueryInterface(long n, T** pp) { VARIANT& var = (*this)[n]; if( !((var.vt & VT_UNKNOWN) || (var.vt & VT_DISPATCH)) ) return E_NOINTERFACE; return var.punkVal->QueryInterface(pp); } }; #endif // INC_COMVECTOR