您的位置:首页 > Web前端

SAFEARRAY使用实例

2012-02-02 22:06 423 查看
目录:
SAFEARRAY使用实例... 1
目录:... 1
前言:... 1
何谓SAFEARRAY:... 1
创建SAFEARRAY:2
方法一:使用SafeArrayAllocDescriptor在栈上创建一维数组... 2
方法二:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建一维数组... 2
方法三:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建二维数组... 3
方法四:使用SafeArrayCreate在堆上创建一维数组... 4
方法五:使用SafeArrayCreate在堆上创建二维数组... 5
方法六:使用SafeArrayCreateEx创建包含结构的一维数组... 5
访问SAFEARRAY:7
方法一:使用SafeArrayAccessData方法... 7
方法二:使用SafeArrayGetElement和SafeArrayPutElement7
CComSafeArray类介绍:... 8
基本的入门例子:8
注意事项:... 8

前言:
SAFEARRAY使用总是困扰很多人,为了把这个问题说个明白,我把我目前掌握的知识做个总结

何谓SAFEARRAY:
SAFEARRAY实际上是一个结构,关于这部分可以参考MSDN。
ms-help://MS.MSDNQTR.2003FEB.2052/automat/htm/chap7_9ntx.htm
我们不需要关心16位操作系统下的定义,因为我们团队只在WIN2000以上平台下开发。

创建SAFEARRAY:
方法一:使用SafeArrayAllocDescriptor在栈上创建一维数组
[align=left] //创建SAFEARRAY数组,每个元素为long型,该数组是一维数组[/align]
[align=left] long nData[10]={1,2,3,4,5,6,7,8,9,10};[/align]
[align=left] [/align]
[align=left] SAFEARRAY* pArray=NULL;[/align]
[align=left] HRESULT hr=SafeArrayAllocDescriptor(1,&pArray);//创建SAFEARRAY结构的对象[/align]
[align=left] pArray->cbElements=sizeof(nData[0]);[/align]
[align=left] pArray->rgsabound[0].cElements=10;[/align]
[align=left] pArray->rgsabound[0].lLbound=0;[/align]
[align=left] pArray->pvData=nData;[/align]
[align=left] pArray->fFeatures=FADF_AUTO|FADF_FIXEDSIZE;//FADF_AUTO指定在栈上分配数据,并且大小不可以改变(固定为10)[/align]
[align=left] [/align]
[align=left] //访问SAFEARRAY数组[/align]
[align=left] long* pValue=NULL;[/align]
[align=left] SafeArrayAccessData(pArray,(void**)&pValue);[/align]
[align=left] long Low(0),High(0);[/align]
[align=left] hr=SafeArrayGetLBound(pArray,1,&Low);//维数索引从1开始[/align]
[align=left] hr=SafeArrayGetUBound(pArray,1,&High);//维数索引从1开始[/align]
[align=left] [/align]
[align=left] SafeArrayUnaccessData(pArray);[/align]
SafeArrayDestroy(pArray);
这种方法在栈上分配数组元素所占的空间,即nData数组所用的空间

方法二:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建一维数组
[align=left] //创建SAFEARRAY数组,每个元素为long型,该数组是一维数组[/align]
[align=left] long nData[10]={1,2,3,4,5,6,7,8,9,10};[/align]
[align=left] [/align]
[align=left] SAFEARRAY* pArray=NULL;[/align]
[align=left] HRESULT hr=SafeArrayAllocDescriptor(1,&pArray);//创建SAFEARRAY结构的对象[/align]
[align=left] pArray->cbElements=sizeof(nData[0]);[/align]
[align=left] pArray->rgsabound[0].cElements=10;[/align]
[align=left] pArray->rgsabound[0].lLbound=0;[/align]
[align=left] SafeArrayAllocData(pArray);[/align]
[align=left] [/align]
[align=left] long* pData=NULL;[/align]
[align=left] SafeArrayAccessData(pArray,(void**)&pData);[/align]
[align=left] long l(0),h(0);[/align]
[align=left] SafeArrayGetLBound(pArray,1,&l);[/align]
[align=left] SafeArrayGetUBound(pArray,1,&h);[/align]
[align=left] long Size=h-l+1;[/align]
[align=left] SafeArrayAccessData(pArray,(void**)&pData);[/align]
[align=left] for(long Idx=l;Idx<Size;++Idx)[/align]
[align=left] {[/align]
[align=left] pData[Idx]=nData[Idx];[/align]
[align=left] }[/align]
[align=left] SafeArrayUnaccessData(pArray);[/align]
[align=left] [/align]
[align=left] //访问SAFEARRAY数组[/align]
[align=left] long* pValue=NULL;[/align]
[align=left] SafeArrayAccessData(pArray,(void**)&pValue);[/align]
[align=left] long Low(0),High(0);[/align]
[align=left] hr=SafeArrayGetLBound(pArray,1,&Low);//维数索引从1开始[/align]
[align=left] hr=SafeArrayGetUBound(pArray,1,&High);//维数索引从1开始[/align]
[align=left] [/align]
[align=left] SafeArrayUnaccessData(pArray);[/align]
SafeArrayDestroy(pArray);

方法三:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建二维数组
[align=left] SAFEARRAY* pArray=NULL;[/align]
[align=left] HRESULT hr=SafeArrayAllocDescriptor(2,&pArray);[/align]
[align=left] pArray->rgsabound[0].lLbound=0;[/align]
[align=left] pArray->rgsabound[0].cElements=3;[/align]
[align=left] pArray->rgsabound[1].lLbound=0;[/align]
[align=left] pArray->rgsabound[1].cElements=3;[/align]
[align=left] [/align]
[align=left] pArray->cbElements=sizeof(long);[/align]
[align=left] hr=SafeArrayAllocData(pArray);[/align]
[align=left] [/align]
[align=left] long lDimension[2];[/align]
[align=left] long x=1;[/align]
[align=left] //为第一行赋值[/align]
[align=left] for(long i=0;i<3;++i)[/align]
[align=left] {[/align]
[align=left] lDimension[1]=0;//行[/align]
[align=left] lDimension[0]=i;//列[/align]
[align=left] SafeArrayPutElement(pArray,lDimension,&x);[/align]
[align=left] x++;[/align]
[align=left] }[/align]
[align=left] //为第二行赋值[/align]
[align=left] for(long i=0;i<3;++i)[/align]
[align=left] {[/align]
[align=left] lDimension[1]=1;//行[/align]
[align=left] lDimension[0]=i;//列[/align]
[align=left] SafeArrayPutElement(pArray,lDimension,&x);[/align]
[align=left] x++;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] //读取SafeArray中第二行第三列的数据[/align]
[align=left] long y(0);[/align]
[align=left] lDimension[1]=1;[/align]
[align=left] lDimension[0]=2;[/align]
[align=left] SafeArrayGetElement(pArray,lDimension,&y);[/align]
[align=left] [/align]
SafeArrayDestroy(pArray);

二维SAFEARRAY数组使用的时候下标要注意,这里采用的是列主序的方式,即lDimension[1]代表行,lDimension[0]代表列。

方法四:使用SafeArrayCreate在堆上创建一维数组
[align=left] SAFEARRAYBOUND Bound[1];[/align]
[align=left] Bound[0].lLbound=0;[/align]
[align=left] Bound[0].cElements=10;[/align]
[align=left] SAFEARRAY* pArray=SafeArrayCreate(VT_I4,1,Bound);[/align]
[align=left] long* pData=NULL;[/align]
[align=left] HRESULT hr=SafeArrayAccessData(pArray,(void**)&pData);[/align]
[align=left] long Low(0),High(0);[/align]
[align=left] SafeArrayGetLBound(pArray,1,&Low);[/align]
[align=left] SafeArrayGetUBound(pArray,1,&High);[/align]
[align=left] long Size=High-Low+1;[/align]
[align=left] for(long Idx=Low;Idx<Size;++Idx)[/align]
[align=left] {[/align]
[align=left] pData[Idx]=Idx;[/align]
[align=left] cout<<pData[Idx]<<endl;[/align]
[align=left] }[/align]
[align=left] SafeArrayUnaccessData(pArray);[/align]
SafeArrayDestroy(pArray);

方法五:使用SafeArrayCreate在堆上创建二维数组
[align=left] SAFEARRAYBOUND Bound[2];[/align]
[align=left] Bound[0].lLbound=0;[/align]
[align=left] Bound[0].cElements=3;[/align]
[align=left] Bound[1].lLbound=0;[/align]
[align=left] Bound[1].cElements=3;[/align]
[align=left] SAFEARRAY* pArray=SafeArrayCreate(VT_I4,2,Bound);[/align]
[align=left] [/align]
[align=left] long Demen[2];[/align]
[align=left] for(long i=0;i<3;++i)[/align]
[align=left] {[/align]
[align=left] for(long j=0;j<3;++j)[/align]
[align=left] {[/align]
[align=left] Demen[1]=i;[/align]
[align=left] Demen[0]=j;[/align]
[align=left] long x=i*j;[/align]
[align=left] SafeArrayPutElement(pArray,Demen,&x);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] //访问二维数组[/align]
[align=left] for(long i=0;i<3;++i)[/align]
[align=left] {[/align]
[align=left] for(long j=0;j<3;++j)[/align]
[align=left] {[/align]
[align=left] Demen[1]=i;[/align]
[align=left] Demen[0]=j;[/align]
[align=left] long x(0);[/align]
[align=left] SafeArrayGetElement(pArray,Demen,&x);[/align]
[align=left] cout<<"("<<i<<","<<j<<") "<<x<<endl;[/align]
[align=left] }[/align]
[align=left] }[/align]
SafeArrayDestroy(pArray);

方法六:使用SafeArrayCreateEx创建包含结构的一维数组
使用SAFEARRAY传递UDT(自定义结构)是一项常用的技术,MSDN文档描述得比较齐全,要注意的一点是,自定义结构要求有自己的GUID,这必须在IDL文件中定义。同时还必须要使用IRecordInfo接口,该接口将和数组一起传递出去,IRecordInfo接口内部记录了UDT的描述信息。
IDL文件中:
[align=left][uuid(810930AA-9229-46e7-B20C-41F6218D0B1A)][/align]
[align=left]struct _BookMarkSchema[/align]
[align=left]{[/align]
[align=left] BSTR Name;[/align]
[align=left] BSTR Context;[/align]
[align=left] BSTR Time;[/align]
};


interface IShape : IDispatch
{
[id(6), helpstring("获取属于某用户的书签名称列表")] HRESULT GetBookMarkName([in] BSTR UserID,[out] SAFEARRAY(struct _BookMarkSchema)* pBookMarkNames);
}

[align=left]library SarstShapeLib[/align]
[align=left]{[/align]
[align=left] [/align]
[align=left] importlib("stdole2.tlb");[/align]
[align=left] [[/align]
[align=left] uuid(DBDCC0F1-38F3-4EB4-A5BD-79A3707BDE9C),[/align]
[align=left] helpstring("Shape Class")[/align]
[align=left] ][/align]
[align=left] coclass Shape[/align]
[align=left] {[/align]
[align=left] [default] interface IShape;[/align]
[align=left] };[/align]
[align=left] struct _BookMarkSchema;[/align]
};

方法的实现为:
[align=left]STDMETHODIMP CShape::GetBookMarkName(BSTR UserID,SAFEARRAY** pBookMarkNames)[/align]
[align=left]{[/align]
[align=left] //获得GIS库信息[/align]
[align=left] CSarstConfigure Configure;[/align]
[align=left] string Flag("GIS");[/align]
[align=left] string IP,Database,UserName,Key,Context;[/align]
[align=left] Configure.GetDatabaseInfo(Flag,IP,Database,UserName,Key,Context);[/align]
[align=left] [/align]
[align=left] //读取图层属性数据[/align]
[align=left] USES_CONVERSION;[/align]
[align=left] string user(CString(UserID).GetBuffer());[/align]
[align=left] string sql("SELECT 书签名,书签描述,时间 FROM 用户书签表 where 用户ID='"+user+"' order by 时间 desc");[/align]
[align=left] FBData data(IP,Database,UserName,Key);[/align]
[align=left] table t=data.GetTable(sql);[/align]
[align=left] if(t.empty())[/align]
[align=left] {[/align]
[align=left] return S_FALSE;[/align]
[align=left] }[/align]
[align=left] //创建SafeArray[/align]
[align=left] IRecordInfo* pRecordInfo=NULL;[/align]
[align=left] HRESULT hr=::GetRecordInfoFromGuids(LIBID_SarstShapeLib,1,0,GetUserDefaultLCID(),IID_STRUCT_BookMarkSchema,&pRecordInfo);[/align]
[align=left] if(FAILED(hr))[/align]
[align=left] return E_FAIL;[/align]
[align=left] *pBookMarkNames=::SafeArrayCreateVectorEx(VT_RECORD,0,long(t.size()-1),(void*)pRecordInfo);[/align]
[align=left] _BookMarkSchema* pData=NULL;[/align]
[align=left] hr=::SafeArrayAccessData(*pBookMarkNames,(void**)&pData);[/align]
[align=left] for(int i=0;i<int(t.size()-1);i++)[/align]
[align=left] {[/align]
[align=left] t[i+1].at(0).CopyTo(&pData[i].Name);[/align]
[align=left] t[i+1].at(1).CopyTo(&pData[i].Context);[/align]
[align=left] t[i+1].at(2).ChangeType(VT_BSTR);[/align]
[align=left] t[i+1].at(2).CopyTo(&pData[i].Time);[/align]
[align=left] }[/align]
[align=left] ::SafeArrayUnaccessData(*pBookMarkNames);[/align]
[align=left] pRecordInfo->Release();[/align]
[align=left] return S_OK;[/align]
}
访问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方法有问题
[align=left]HRESULT SetAt(LONG lIndex, const T& t, BOOL bCopy = TRUE)[/align]
[align=left] {[/align]
[align=left] bCopy;[/align]
[align=left] ATLASSERT(m_psa != NULL);[/align]
[align=left] if(m_psa == NULL)[/align]
[align=left] return E_FAIL;[/align]
[align=left] [/align]
[align=left] LONG lLBound = GetLowerBound();[/align]
[align=left] ATLASSERT(lIndex >= lLBound);[/align]
[align=left] ATLASSERT(lIndex <= GetUpperBound());[/align]
[align=left] if ((lIndex < lLBound) || (lIndex > GetUpperBound()))[/align]
[align=left] return E_INVALIDARG;[/align]
[align=left] [/align]
[align=left] ((T*)m_psa->pvData)[lIndex-lLBound] = t;[/align]
[align=left] return S_OK;[/align]
[align=left]我们可以看到,MSDN中描述的bCopy如果为true将创建数据的副本,在代码中并没有实现,事实上bCopy参数并没有起到任何作用,不知道怎么会出现这样的错误。因此,如果我们在添加BSTR或者VARIANT类型的元素时必须先复制一份副本,然后传递给Add方法(Add方法内部调用Set方法)。[/align]
[align=left]比如:m_sa.Add(CComVariant(bstr));[/align]
[align=left]看到这里我还有一个奇怪的地方[/align]
[align=left]((T*)m_psa->pvData)[lIndex-lLBound] = t;并没有使用SafeArrayAccessData方法获取指针,实际上SafeArrayAccessData和直接使用m_psa->pvData方法的区别在于前者要增加引用计数,而后者不会增加。因为这里如果使用SafeArrayAccessData的话,必然也要使用SafeArrayUnaccessData方法,为了效率这里省略。[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: