template <typename T, VARTYPE _vartype = _ATL_AutomationType<T>::type> class CComSafeArray { ... public: LPSAFEARRAY m_psa; }
m_psa是CComSafeArray唯一的state
#define DEFINE_AUTOMATION_TYPE_FUNCTION(ctype, typewrapper, oleautomationtype) \ template <> \ struct _ATL_AutomationType<ctype> { \ typedef typewrapper _typewrapper;\ enum { type = oleautomationtype }; \ static void* GetT(const T& t) { \ return (void*)&t; \ } \ };
DEFINE_AUTOMATION_TYPE_FUNCTION用于将C++类型转换成VARTYPE
DEFINE_AUTOMATION_TYPE_FUNCTION(CHAR, CHAR, VT_I1) DEFINE_AUTOMATION_TYPE_FUNCTION(SHORT, SHORT, VT_I2) DEFINE_AUTOMATION_TYPE_FUNCTION(INT, INT, VT_I4) DEFINE_AUTOMATION_TYPE_FUNCTION(LONG, LONG, VT_I4) DEFINE_AUTOMATION_TYPE_FUNCTION(LONGLONG, LONGLONG, VT_I8) DEFINE_AUTOMATION_TYPE_FUNCTION(BYTE, BYTE, VT_UI1) DEFINE_AUTOMATION_TYPE_FUNCTION(USHORT, USHORT, VT_UI2) DEFINE_AUTOMATION_TYPE_FUNCTION(UINT, UINT, VT_UI4 DEFINE_AUTOMATION_TYPE_FUNCTION(ULONG, ULONG, VT_UI4) DEFINE_AUTOMATION_TYPE_FUNCTION(ULONGLONG, ULONGLONG, VT_UI8) DEFINE_AUTOMATION_TYPE_FUNCTION(FLOAT, FLOAT, VT_R4) DEFINE_AUTOMATION_TYPE_FUNCTION(DOUBLE, DOUBLE, VT_R8) DEFINE_AUTOMATION_TYPE_FUNCTION(DECIMAL, DECIMAL, VT_DECIMAL) DEFINE_AUTOMATION_TYPE_FUNCTION(VARIANT, CComVariant, VT_VARIANT) DEFINE_AUTOMATION_TYPE_FUNCTION(CY, CY, VT_CY)
于是_ATL_Automation_Type<long>::type=VT_I4
Constructors and Destructor
template只是确定array的元素类型,还需要确定维数和各维的size
有7个不同的构造函数用于这个工作,除了第一个Default无参构造函数外(m_psa=NULL),其余的函数都将使用到CComSafeArrayBound用于确定这些细节:
class CComSafeArrayBound : public SAFEARRAYBOUND { CComSafeArrayBound(ULONG ulCount = 0, LONG lLowerBound = 0) { ... } CComSafeArrayBound& operator=(const CComSafeArrayBound& bound) { ... } CComSafeArrayBound& operator=(ULONG ulCount) { ... } ULONG GetCount() const { ... } ULONG SetCount(ULONG ulCount) { ... } LONG GetLowerBound() const { ... } LONG SetLowerBound(LONG lLowerBound) { ... } LONG GetUpperBound() const { ... } };
例如:
explicit CComSafeArray(ULONG ulCount, LONG lLBound = 0) : m_psa(NULL) { CComSafeArrayBound bound(ulCount, lLBound); HRESULT hRes = Create(&bound); if (FAILED(hRes)) AtlThrow(hRes); }
Create是一层API的helper:
HRESULT Create(const SAFEARRAYBOUND *pBound, UINT uDims = 1) { ATLASSERT(m_psa == NULL); ATLASSERT(uDims > 0); HRESULT hRes = S_OK; m_psa = SafeArrayCreate(_vartype, uDims, const_cast<LPSAFEARRAYBOUND>(pBound)); if (NULL == m_psa) hRes = E_OUTOFMEMORY; else hRes = Lock(); return hRes; }
建立1维数组的例子:
// create a 1-D zero-based SAFEARRAY of long with 10 elements CComSafeArray<long> sa(10); // create a 1-D one-based SAFEARRAY of double with 5 elements CComSafeArray<double> sa(5,1);
也可以使用这个来建立1D数组:
explicit CComSafeArray(const SAFEARRAYBOUND& bound);
这个例子就不如上述例子简洁:
CComSafeArrayBound bound(5,1); // 1-D one-based array CComSafeArray<long> sa(bound);
使用下面的函数来建立多维数组:
explicit CComSafeArray(const SAFEARRAYBOUND *pBound, UINT uDims = 1);
例子:
// 3-D array with all dimensions
// left-most dimension has 3 elements
CComSafeArrayBound bound1(3);
// middle dimension has 4 elements
CComSafeArrayBound bound2(4);
// right-most dimension has 5 elements
CComSafeArrayBound bound3(5);
// equivalent C-style array indices would be [3][4][5]
CComSafeArrayBound rgBounds[] = { bound1, bound2, bound3 };
CComSafeArray<int> sa(rgBounds, 3);
从已有数组:
CComSafeArray(const SAFEARRAY *psaSrc) : m_psa(NULL); CComSafeArray(const SAFEARRAY& saSrc) : m_psa(NULL); CComSafeArray(const CComSafeArray& saSrc) : m_psa(NULL);
例子:
CComSafeArray<int> saSrc(5); // source is 1-D array of 5 ints // allocate storage for 1-D array of 5 ints // and copy contents of source CComSafeArray<int> saDest(saSrc);
析构函数调用Detroy():
HRESULT Destroy() { HRESULT hRes = S_OK; if (m_psa != NULL) { hRes = Unlock(); if (SUCCEEDED(hRes)) { hRes = SafeArrayDestroy(m_psa); if (SUCCEEDED(hRes)) m_psa = NULL; } } return hRes; }
请注意Unlock()函数,用于释放SAFEARRAY的计数
Assignment
CComSafeArray<T>& operator=(const CComSafeArray& saSrc) { *this = saSrc.m_psa; return *this; } CComSafeArray<T>& operator=(const SAFEARRAY *psaSrc) { ATLASSERT(psaSrc != NULL); HRESULT hRes = CopyFrom(psaSrc); if (FAILED(hRes)) AtlThrow(hRes); return *this; }
CopyFrom会先释放内部m_psa
例子:
CComSafeArray<long> sa1(10); // do something interesting with sa1 CComSafeArray<long> sa2(5); // free contents of sa1, duplicate contents // of sa2 and put into sa1 sa1 = sa2;
The Detach and Attach Methods
HRESULT Attach(const SAFEARRAY *psaSrc) { ATLENSURE_THROW(psaSrc != NULL, E_INVALIDARG); VARTYPE vt; HRESULT hRes = ::ATL::AtlSafeArrayGetActualVartype( const_cast<LPSAFEARRAY>(psaSrc), &vt); ATLENSURE_SUCCEEDED(hRes); ATLENSURE_THROW(vt == GetType(), E_INVALIDARG); hRes = Destroy(); m_psa = const_cast<LPSAFEARRAY>(psaSrc); hRes = Lock(); return hRes; } LPSAFEARRAY Detach() { Unlock(); LPSAFEARRAY pTemp = m_psa; m_psa = NULL; return pTemp; }
注意Destroy()也用到Unlock()方法,这个属于Win16的遗留
例如以下代码是错误的:
SAFEARRAY *psa = ::SafeArrayCreateVector(VT_I4, 0, 10); // BAD - this pointer may not be valid! int *pData = reinterpret_cast<int *>(pda->pvData); // BOOM (maybe) pData[0] = 5;
应该使用Lock:
SAFEARRAY *psa = ::SafeArrayCreateVector(VT_I4, 0, 10); // GOOD - this will allocate the actual storage for the data ::SafeArrayLock(psa); // Now the pointer is valid int *pData = ( int * )(pda->pvData); pData[0] = 5; // Unlock after we're done ::SafeArrayUnlock( psa );
实际上Lock为SAFEARRAY分配存储空间,并且设置pvData。CComSafeArrayy构造函数调用Lock(),析构函数调用Destroy(),后者调用Unlock()。
Attach用于处理[in]SAFEARRAY,例如:
STDMETHODIMP SomeClass::AverageArray(/* [in] */ SAFEARRAY* psa,
/* [out] */ LONG* plAvg) {
if (!plAvg) return E_POINTER;
CComSafeArray<long> sa; // Note: no type check is done
// against psa type
sa.Attach(psa); // we're pointing at the same
// memory as psa
... perform some calculations
sa.Detach(); // Must detach here or risk a crash
return S_OK;
}
Detach用于处理[out]SAFEARRAY,例如:
STDMETHODIMP SomeClass::get_Array(/* [out] */ SAFEARRAY** ppsa) {
if (!ppsa) return E_POINTER;
CComSafeArray<long> sa(10);
... populate sa instance
// no resources released when we leave scope
// and no copying performed
*ppsa = sa.Detach();
return S_OK;
}
Attach/Detach不会做数据的Copy,下边的代码是错误的:
STDMETHODIMP SomeClass::DontDoThis(SAFEARRAY* psa) {
// We have two references to the safearray
CComSafeArray<long> sa1, sa2;
sa1.Attach(psa);
sa2.Attach(psa);
// manipulate the array here
// BUG: Don't do this
sa2.Destroy( );
}
千万不要对同一SAFEARRAY采用多个Attach
CComSafeArray Operations
LONG GetLowerBound(UINT uDim = 0) const; LONG GetUpperBound(UINT uDim = 0) const; ULONG GetCount(UINT uDim = 0) const; UINT GetDimensions() const; VARTYPE GetType() const ; bool IsSizable() const;
其中类成员fFeatures控制IsSizable(),默认应该=TRUE
LPSAFEARRAY* GetSafeArrayPtr() { return &m_psa; }
HRESULT Resize(ULONG ulCount, LONG lLBound = 0); HRESULT Resize(const SAFEARRAYBOUND *pBound);
HRESULT Resize(const SAFEARRAYBOUND *pBound) { ATLASSUME(m_psa != NULL); ATLASSERT(pBound != NULL); if (!IsSizable()) { return E_FAIL; } HRESULT hRes = Unlock(); if (SUCCEEDED(hRes)) { hRes = SafeArrayRedim(m_psa, const_cast<LPSAFEARRAYBOUND>(pBound)); if (SUCCEEDED(hRes)) { hRes = Lock(); } } return hRes; }
这个函数有bug,应该检查返回的HRESULT
HRESULT Add(const T& t, BOOL bCopy = TRUE); HRESULT Add(ULONG ulCount, const T *pT, BOOL bCopy = TRUE); HRESULT Add(const SAFEARRAY *psaSrc);
Add流程:Create,Resize,SetAt
CComSafeArray Element Accessors
const typename _ATL_AutomationType<T>::_typewrapper& GetAt(LONG lIndex) const { ATLASSUME(m_psa != NULL); if(m_psa == NULL) AtlThrow(E_FAIL); LONG lLBound = GetLowerBound(); ATLASSERT(lIndex >= lLBound); ATLASSERT(lIndex <= GetUpperBound()); if ((lIndex < lLBound) || (lIndex > GetUpperBound())) AtlThrow(E_INVALIDARG); return ( (_ATL_AutomationType<T>::_typewrapper*) m_psa->pvData )[lIndex-lLBound]; } _ATL_AutomationType<T>::_typewrapper& GetAt(LONG lIndex) { // code identical to const version }
HRESULT SetAt(LONG lIndex, const T& t, BOOL bCopy = TRUE) { bCopy; ATLASSERT(m_psa != NULL); LONG lLBound = GetLowerBound(); ATLASSERT(lIndex >= lLBound); ATLASSERT(lIndex <= GetUpperBound()); ((T*)m_psa->pvData)[lIndex-lLBound] = t; return S_OK; }
例子:
CComSafeArray<long> sa(5); long lNewVal = 14; // replace the 4th element with the value 14 sa.SetAt(3, lNewVal);
template<> HRESULT CComSafeArray<BSTR>::SetAt(LONG lIndex, const BSTR& strData, BOOL bCopy) { // validation code omitted for clarity BSTR strOrg = ((BSTR*)m_psa->pvData)[lIndex-lLBound]; if (strOrg) ::SysFreeString(strOrg); if (bCopy) { BSTR strTemp = ::SysAllocString(strData); if (NULL == strTemp) return E_OUTOFMEMORY; ((BSTR*)m_psa->pvData)[lIndex-lLBound] = strTemp; } else ((BSTR*)m_psa->pvData)[lIndex-lLBound] = strData; return S_OK; }
例子:
BSTR bstr1 = ::SysAllocString(OLESTR("Go Longhorns!"));
BSTR bstr2 = ::SysAllocString(OLESTR("ATL Rocks!"));
CComSafeArray<BSTR> sa(5);
sa.SetAt(2, bstr1, true); // sa generates its own copy of bstr1
sa.SetAt(3, bstr2, false); // sa assigns element to bstr2
::SysFreeString(bstr1); // ok, sa still has a copy
::SysFreeString(bstr2); // wrong!!! we don't own bstr2
例子:
IUnknown* pUnk1, pUnk2;
// assign both pointers to refer to an object
CComSafeArray<IUnknown*> sa(5);
sa.SetAt(2, pUnk1, true); // sa calls AddRef on pUnk1
sa.SetAt(3, pUnk2, false); // sa assigns element to pUnk2
// without AddRefing
pUnk1->Release(); // ok, refcount non-zero because
// of sa AddRef
pUnk2->Release(); // wrong!!! we don't own pUnk2
HRESULT MultiDimGetAt(const LONG *alIndex, T& t) { ATLASSERT(m_psa != NULL); return SafeArrayGetElement(m_psa, const_cast<LONG*>(alIndex), &t); } HRESULT MultiDimSetAt(const LONG *alIndex, const T& t) { ATLASSERT(m_psa != NULL); return SafeArrayPutElement(m_psa, const_cast<LONG*>(alIndex), _ATL_AutomationType<T>::GetT(t)); }
例子:
// 2-D array with all dimensions
// left-most dimension has 3 elements
CComSafeArrayBound bound1(3);
// right-most dimension has 4 elements
CComSafeArrayBound bound2(4);
// equivalent C-style array indices would be [3][4]
CComSafeArrayBound rgBounds[] = { bound1, bound2 };
CComSafeArray<int> sa(rgBounds, 2);
int rgIndElement1[] = { 0, 1 }; // access element at sa[1][0]
int rgIndElement2[] = { 3, 2 }; // access element at sa[2][3]
long lVal = 0;
// retrieve value at sa[1][0]
sa.MultiDimGetAt(rgIndElement1, lVal);
// multiply value by 2 and store it
// in element located at sa[2][3]
sa.MultiDimSetAt(rgIndElement2, lVal*=2);
CComSafeArray Operators
const typename _ATL_AutomationType<T>::_typewrapper& operator[](int nIndex) const { return GetAt(nIndex); } typename _ATL_AutomationType<T>::_typewrapper& operator[](int nIndex) { return GetAt(nIndex); } const typename _ATL_AutomationType<T>::_typewrapper& operator[](LONG nIndex) const { return GetAt(nIndex); } typename _ATL_AutomationType<T>::_typewrapper& operator[](LONG nIndex) { return GetAt(nIndex); }
例子:
CComSafeArray<int> sa(5); ATLASSERT(sa[2] == 0); sa[2] = 17; ATLASSERT(sa[2] == 17);
operator const SAFEARRAY *() const { return m_psa; } operator LPSAFEARRAY() { return m_psa; }
CComSafeArray并未定义operator&(),所以下面的代码会发生编译错误:
HRESULT CreateANewSafeArray( SAFEARRAY** ppsa ) {
*ppsa = SafeArrayCreateVector(VT_I4, 1, 15 );
return S_OK;
}
HRESULT UseCreatedSafeArray( ) {
CComSafeArray< int > sa;
HRESULT hr = CreateANewSafeArray( &sa );
}
可以使用:
HRESULT UseCreatedSafeArray( ) {
SAFEARRAY *psa = null;
HRESULT hr = CreateANewSafeArray( &psa );
CComSafeArray< int > sa;
sa.Attach( psa );
}
也可以:
HRESULT UseCreatedSafeArray( ) {
CComSafeArray< int > sa;
HRESULT hr = CreateANewSafeArray(sa.GetSafeArrayPtr());
}
本文详细介绍了CComSafeArray类的使用方法,包括构造函数、析构函数、赋值操作及Attach/Detach方法等。此外,还讲解了如何进行多维数组的操作以及一些重要的注意事项。

1882

被折叠的 条评论
为什么被折叠?



