ATL Internals 2ed复习.chapter 3.CComSafeArray

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());
}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值