SAFEARRAY使用

http://blog.csdn.net/zssureqh/article/details/7611380


 
SAFEARRAY使用總是困擾很多人,爲了把這個問題說個明白,我把我目前掌握的知識做個總結
 
SAFEARRAY實際上是一個結構,關於這部分可以參考MSDN。
ms-help://MS.MSDNQTR.2003FEB.2052/automat/htm/chap7_9ntx.htm
我們不需要關心16位操作系統下的定義,因爲我們團隊只在WIN2000以上平臺下開發。
 
方法一:使用SafeArrayAllocDescriptor在棧上創建一維數組
     //創建SAFEARRAY數組,每個元素爲long型,該數組是一維數組
     long nData[10]={1,2,3,4,5,6,7,8,9,10};
 
     SAFEARRAY* pArray=NULL;
     HRESULT hr=SafeArrayAllocDescriptor(1,&pArray);//創建SAFEARRAY結構的對象
     pArray->cbElements=sizeof(nData[0]);
     pArray->rgsabound[0].cElements=10;
     pArray->rgsabound[0].lLbound=0;
     pArray->pvData=nData;
     pArray->fFeatures=FADF_AUTO|FADF_FIXEDSIZE;//FADF_AUTO指定在棧上分配數據,並且大小不可以改變(固定爲10)
    
     //訪問SAFEARRAY數組
     long* pValue=NULL;
     SafeArrayAccessData(pArray,(void**)&pValue);
     long Low(0),High(0);
     hr=SafeArrayGetLBound(pArray,1,&Low);//維數索引從1開始
     hr=SafeArrayGetUBound(pArray,1,&High);//維數索引從1開始
 
     SafeArrayUnaccessData(pArray);
     SafeArrayDestroy(pArray);
這種方法在棧上分配數組元素所佔的空間,即nData數組所用的空間
 
 
方法二:使用SafeArrayAllocDescriptorSafeArrayAllocData在堆上創建一維數組
     //創建SAFEARRAY數組,每個元素爲long型,該數組是一維數組
     long nData[10]={1,2,3,4,5,6,7,8,9,10};
 
     SAFEARRAY* pArray=NULL;
     HRESULT hr=SafeArrayAllocDescriptor(1,&pArray);//創建SAFEARRAY結構的對象
     pArray->cbElements=sizeof(nData[0]);
     pArray->rgsabound[0].cElements=10;
     pArray->rgsabound[0].lLbound=0;
     SafeArrayAllocData(pArray);
 
     long* pData=NULL;
     SafeArrayAccessData(pArray,(void**)&pData);
     long l(0),h(0);
     SafeArrayGetLBound(pArray,1,&l);
     SafeArrayGetUBound(pArray,1,&h);
     long Size=h-l+1;
     SafeArrayAccessData(pArray,(void**)&pData);
     for(long Idx=l;Idx<Size;++Idx)
     {
         pData[Idx]=nData[Idx];
     }
     SafeArrayUnaccessData(pArray);
 
     //訪問SAFEARRAY數組
     long* pValue=NULL;
     SafeArrayAccessData(pArray,(void**)&pValue);
     long Low(0),High(0);
     hr=SafeArrayGetLBound(pArray,1,&Low);//維數索引從1開始
     hr=SafeArrayGetUBound(pArray,1,&High);//維數索引從1開始
 
     SafeArrayUnaccessData(pArray);
     SafeArrayDestroy(pArray);
 
 
方法三:使用SafeArrayAllocDescriptorSafeArrayAllocData在堆上創建二維數組
       SAFEARRAY* pArray=NULL;
     HRESULT hr=SafeArrayAllocDescriptor(2,&pArray);
     pArray->rgsabound[0].lLbound=0;
     pArray->rgsabound[0].cElements=3;
     pArray->rgsabound[1].lLbound=0;
     pArray->rgsabound[1].cElements=3;
 
     pArray->cbElements=sizeof(long);
     hr=SafeArrayAllocData(pArray);
 
     long lDimension[2];
     long x=1;
     //爲第一行賦值
     for(long i=0;i<3;++i)
     {
         lDimension[1]=0;//
         lDimension[0]=i;//
         SafeArrayPutElement(pArray,lDimension,&x);
         x++;
     }
     //爲第二行賦值
     for(long i=0;i<3;++i)
     {
         lDimension[1]=1;//
         lDimension[0]=i;//
         SafeArrayPutElement(pArray,lDimension,&x);
         x++;
     }
    
     //讀取SafeArray中第二行第三列的數據
     long y(0);
     lDimension[1]=1;
     lDimension[0]=2;
     SafeArrayGetElement(pArray,lDimension,&y);
 
     SafeArrayDestroy(pArray);
 
二維SAFEARRAY數組使用的時候下標要注意,這裏採用的是列主序的方式,即lDimension[1]代表行,lDimension[0]代表列。
 
 
 
方法四:使用SafeArrayCreate在堆上創建一維數組
     SAFEARRAYBOUND Bound[1];
     Bound[0].lLbound=0;
     Bound[0].cElements=10;
     SAFEARRAY* pArray=SafeArrayCreate(VT_I4,1,Bound);
     long* pData=NULL;
     HRESULT hr=SafeArrayAccessData(pArray,(void**)&pData);
     long Low(0),High(0);
     SafeArrayGetLBound(pArray,1,&Low);
     SafeArrayGetUBound(pArray,1,&High);
     long Size=High-Low+1;
     for(long Idx=Low;Idx<Size;++Idx)
     {
         pData[Idx]=Idx;
         cout<<pData[Idx]<<endl;
     }
     SafeArrayUnaccessData(pArray);
     SafeArrayDestroy(pArray);
 
方法五:使用SafeArrayCreate在堆上創建二維數組
     SAFEARRAYBOUND Bound[2];
     Bound[0].lLbound=0;
     Bound[0].cElements=3;
     Bound[1].lLbound=0;
     Bound[1].cElements=3;
     SAFEARRAY* pArray=SafeArrayCreate(VT_I4,2,Bound);
    
     long Demen[2];
     for(long i=0;i<3;++i)
     {
         for(long j=0;j<3;++j)
         {
              Demen[1]=i;
              Demen[0]=j;
              long x=i*j;
              SafeArrayPutElement(pArray,Demen,&x);
         }
     }
 
     //訪問二維數組
     for(long i=0;i<3;++i)
     {
         for(long j=0;j<3;++j)
         {
              Demen[1]=i;
              Demen[0]=j;
              long x(0);
              SafeArrayGetElement(pArray,Demen,&x);
              cout<<"("<<i<<","<<j<<") "<<x<<endl;
         }
     }
     SafeArrayDestroy(pArray);
 
方法六:使用SafeArrayCreateEx創建包含結構的一維數組
使用SAFEARRAY傳遞UDT(自定義結構)是一項常用的技術,MSDN文檔描述得比較齊全,要注意的一點是,自定義結構要求有自己的GUID,這必須在IDL文件中定義。同時還必須要使用IRecordInfo接口,該接口將和數組一起傳遞出去,IRecordInfo接口內部記錄了UDT的描述信息。
IDL文件中:
[uuid(810930AA-9229-46e7-B20C-41F6218D0B1A)]
struct _BookMarkSchema
{
     BSTR Name;
     BSTR Context;
     BSTR Time;
};
 
interface IShape : IDispatch
{
[id(6), helpstring("獲取屬於某用戶的書籤名稱列表")] HRESULT GetBookMarkName([in] BSTR UserID,[out] SAFEARRAY(struct _BookMarkSchema)* pBookMarkNames);
}
 
 
library SarstShapeLib
{
    
     importlib("stdole2.tlb");
     [
         uuid(DBDCC0F1-38F3-4EB4-A5BD-79A3707BDE9C),
         helpstring("Shape Class")
     ]
     coclass Shape
     {
         [defaultinterface IShape;
     };
     struct _BookMarkSchema;
};
 
 
方法的實現爲:
STDMETHODIMP CShape::GetBookMarkName(BSTR UserID,SAFEARRAY** pBookMarkNames)
{
     //獲得GIS庫信息
     CSarstConfigure Configure;
     string Flag("GIS");
     string IP,Database,UserName,Key,Context;
     Configure.GetDatabaseInfo(Flag,IP,Database,UserName,Key,Context);
 
     //讀取圖層屬性數據
     USES_CONVERSION;
     string user(CString(UserID).GetBuffer());
     string sql("SELECT 書籤名,書籤描述,時間 FROM 用戶書籤表 where 用戶ID='"+user+"' order by 時間 desc");
     FBData data(IP,Database,UserName,Key);
     table t=data.GetTable(sql);
     if(t.empty())
     {
         return S_FALSE;
     }
     //創建SafeArray
     IRecordInfo* pRecordInfo=NULL;
     HRESULT hr=::GetRecordInfoFromGuids(LIBID_SarstShapeLib,1,0,GetUserDefaultLCID(),IID_STRUCT_BookMarkSchema,&pRecordInfo);
     if(FAILED(hr))
         return E_FAIL;
     *pBookMarkNames=::SafeArrayCreateVectorEx(VT_RECORD,0,long(t.size()-1),(void*)pRecordInfo);
     _BookMarkSchema* pData=NULL;
     hr=::SafeArrayAccessData(*pBookMarkNames,(void**)&pData);
     for(int i=0;i<int(t.size()-1);i++)
     {
         t[i+1].at(0).CopyTo(&pData[i].Name);
         t[i+1].at(1).CopyTo(&pData[i].Context);
         t[i+1].at(2).ChangeType(VT_BSTR);
         t[i+1].at(2).CopyTo(&pData[i].Time);
     }
     ::SafeArrayUnaccessData(*pBookMarkNames);
     pRecordInfo->Release();
     return S_OK;
}
訪問SAFEARRAY:
這種方法可以參見創建SAFEARRAY之方法一
請注意,訪問完後要調用SafeArrayUnaccessData方法,並且調用SafeArrayDestroy銷燬數組
這種方式通常用於訪問一位數組
這種方法可以參見創建SAFEARRAY之方法五
這種方式在訪問多維數組的時候很有用
 
CComSafeArray類介紹:
可以參見下面的MSDN鏈接
ms-help://MS.MSDNQTR.2003FEB.2052/vclib/html/vclrfCComSafeArray.htm
注意,我個人認爲本例有錯,應該最後加上一句代碼delete pSar;
因爲雖然pvData指針指向的內存是在堆中,但是tagSAFEARRAY結構對象生存在new開闢的堆上,如果不delete的話,將會內存泄漏。
 
 
1) SetAt方法有問題
HRESULT SetAt(LONG lIndex, const T& t, BOOL bCopy = TRUE)
     {
         bCopy;
         ATLASSERT(m_psa != NULL);
         if(m_psa == NULL)
              return E_FAIL;
             
         LONG lLBound = GetLowerBound();
         ATLASSERT(lIndex >= lLBound);
         ATLASSERT(lIndex <= GetUpperBound());
         if ((lIndex < lLBound) || (lIndex > GetUpperBound()))
              return E_INVALIDARG;
             
         ((T*)m_psa->pvData)[lIndex-lLBound] = t;
         return S_OK;
我們可以看到,MSDN中描述的bCopy如果爲true將創建數據的副本,在代碼中並沒有實現,事實上bCopy參數並沒有起到任何作用,不知道怎麼會出現這樣的錯誤。因此,如果我們在添加BSTR或者VARIANT類型的元素時必須先複製一份副本,然後傳遞給Add方法(Add方法內部調用Set方法)。
比如:m_sa.Add(CComVariant(bstr));
看到這裏我還有一個奇怪的地方
((T*)m_psa->pvData)[lIndex-lLBound] = t;並沒有使用SafeArrayAccessData方法獲取指針,實際上SafeArrayAccessData和直接使用m_psa->pvData方法的區別在於前者要增加引用計數,而後者不會增加。因爲這裏如果使用SafeArrayAccessData的話,必然也要使用SafeArrayUnaccessData方法,爲了效率這裏省略。

發佈了30 篇原創文章 · 獲贊 6 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章