1. 程式人生 > 其它 >SAFEARRAY 使用轉載

SAFEARRAY 使用轉載

SAFEARRAY實際上是一個結構,關於這部分可以參考MSDN。

typedef struct tagSAFEARRAY {
  USHORT         cDims;
  USHORT         fFeatures;
  ULONG          cbElements;
  ULONG          cLocks;
  PVOID          pvData;
  SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY;

建立SAFEARRAY
方法一:使用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陣列所用的空間 方法二:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上建立一維陣列 // 建立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); int nLen = h - l + 1; for (int i = 0; i < nLen; ++i) { pData[i] = nData[i]; } 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開始 CString str = _T("["); for (int i = 0; i < High - Low + 1; ++i) { long value = pValue[i]; str += (" " + std::to_string(value)).c_str(); } str += _T("]"); SafeArrayUnaccessData(pArray); SafeArrayDestroy(pArray); 方法三:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上建立二維陣列 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 { [default] interface 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 方法一:使用SafeArrayAccessData方法 這種方法可以參見 建立SAFEARRAY之方法一 請注意,訪問完後要呼叫SafeArrayUnaccessData方法,並且呼叫SafeArrayDestroy銷燬陣列 這種方式通常用於訪問一位陣列 方法二:使用SafeArrayGetElement和SafeArrayPutElement 這種方法可以參見 建立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方法,為了效率這裡省略。 ———————————————— 版權宣告:本文為CSDN博主「htj10」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。 原文連結:https://blog.csdn.net/htj10/article/details/110441227