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
标签:SAFEARRAY,hr,pArray,long,数组,使用,rgsabound,转载 From: https://www.cnblogs.com/nanfei/p/16978105.html