COM中使用的数组-SafeArray
来源:互联网 发布:微信怎么发布淘宝链接 编辑:程序博客网 时间:2024/04/30 10:14
1 使用SafeArray
SafeArray是VB中的数组存储方式。通过SafeArray,可以在VC++和VB间相互调用。SafeArray也是Automation中的标准数组存储方式。
1.1 SafeArray处理函数
COM提供了一套API用于处理SafeArray。为了保证程序和SafeArray结构无关,程序中建立、读取、更改和释放SafeArray都应该通过这些API进行,而不应该直接读写SafeArray结构。下面介绍常用的SafeArray处理函数。
1.1.1 建立SafeArray
建立多维普通数组:
SAFEARRAY* SafeArrayCreate(VARTYPE vt,unsigned int cDims,SAFEARRRAYBOUND * rgsabound);
用于建立多维自定义类型或接口指针数组:
SAFEARRAY SafeArrayCreateEx(VARTYPE vt,unsigned int cDims,SAFEARRRAYBOUND * rgsabound, PVOID pvExtra);
用于建立一维普通数组:
SAFEARRAY* SafeArrayCreateVector(VARTYPE vt,long lLbound,unsigned int cElements);
用于建立一维自定义类型或接口指针数组:
SAFEARRAY* SafeArrayCreateVectorEx(VARTYPE vt,long lLbound,unsigned int cElements,LPVOID pvExtra);
1.1.2 释放数组
HRESULT SafeArrayDestroy(SAFEARRAY * psa);SafeArrayDestroy用于释放创建的SafeArray数组。
1.1.3 访问数据
HRESULT SafeArrayAccessData(SAFEARRAY * psa,void HUGEP ** ppvData);
HRESULT SafeArrayUnaccessData(SAFEARRAY * psa);
SafeArrayAccessData函数返回数组的指针。而SafeArrayUnaccessData释放通过SafeArrayAccessData所取得的指针。
1.2 SafeArray相关处理
1.2.1 创建SafeArray数组
创建SafeArray可以使用COM提供的四个创建函数之一。所有的创建函数都返回一个SafeArray指针。通过这个指针可以读写SafeArray中的数据。SafeArray使用完后必须释放。
1. SafeArrayCreateVector
SAFEARRAY* SafeArrayCreateVector(VARTYPE vt, long lLbound, unsigned int cElements);
这个函数用来创建简单类型的一维数组。这个函数有三个参数:vt是数组类型、lLbound是数组下界值(最小下标)和数组长度。
vt的取值如下表: vt值 类型
VT_UI1 无符号1字节整数(BYTE)数组
VT_UI2 无符号2字节整数(WORD)数组
VT_UI4 无符号4字节整数(DWORD)数组
VT_UINT 无符号整数(UINT)数组
VT_INT 有符号整数(INT)数组
VT_I1 有符号1字节整数数组
VT_I2 有符号2字节整数数组
VT_I4 有符号4字节整数数组
VT_R4 IEEE 4字节浮点数(float)数组
VT_R8 IEEE 8字节浮点数(double)数组
VT_CY 8字节定点数货币值数组
VT_BSTR VB 字符串数组
VT_DECIMAL 12 字节定点数(大数字)数组
VT_ERROR 标准错误编号数组
VT_BOOL 布尔值数组
VT_DATE 日期型数组
VT_VARIANT VB Variant类型数组
lLbound是数组的最小下标,可以是取负数。cElements是数组的长度。数组的最大下标的值是最小下标加上数组长度减一。
SafeArrayCreateVector函数返回SafeArray结构的指针。
2. SafeArrayCreateVectorEx
SAFEARRAY* SafeArrayCreateVectorEx(VARTYPE vt, long lLbound, unsigned int cElements,LPVOID pvExtra);
这个函数用于创建自定义类型或COM对象的SafeArray数组。和SafeArrayCreateVector类似,SafeArrayCreateVector也有类型、下界和长度的三个参数。SafeArrayCreateVectorEx还增加了一个参数pvExtra。pvExtra的含义和vt的取值有关。当vt的取值在上表中的时候,pvExtra的取值没有作用。当vt取值VT_RECORD时,SafeArrayCreateVectorEx返回一个自定义类型(结构structure或联合union)的数组。这时,pvExtra必须是一个指向IRecordInfo的指针。
当vt取值是VT_UNKNOWN或VT_DISPATCH时。pvExtra是一个指向IID(接口GUID)的指针。在目前的COM规范中,pvExtra只能是IID_IUnknown和IID_IDispatch。并且必须和vt的取值一致。
a. 创建自定义类型数组
当vt是VT_RECORD时。pvExtra必须是一个IRecordInfo指针。绝大多数情况下,我们从TLB中取得自定义类型的IRecordInfo指针。以下是取得IRecordInfo的代码: IRecordInfo * pRecordInfo;hr = GetRecordInfoFromGuids(LibID,MajorVer,MinorVer,LOCALE_USER_DEFAULT, TypeGUID,&pRecordInfo); 上述代码中,LibID是所TLB的GUID,MajorVer和MinorVer分别是TLB的主、次版本号,TypeGUID是自定义结构的GUID。函数返回的是IRecordInfo接口的指针。
b. 创建COM对象数组
当需要创建COM数组时,可以使用IUnknown指针,也可以用IDispatch指针。如果需要使用其它指针类型,应该使用QueryInterface方法取得,而不能直接在数组中保存。因为SafeArray数组的序列化程序只能处理IUnknown和IDispatch两种指针类型,如果在数组中放其它接口类型的指针,可能在跨套间使用中会出现问题。
1.2.2 读取和写入SafeArray数组。
读写SafeArray数组时。应该使用COM提供的标准API。COM提供了大量函数用于SafeArray数组的操作,本文中仅使用其中的两个函数,SafeArrayAccessData和SafeArrayUnaccessData,和一些辅助用的函数。实际上是用这两个函数就可以进行所有的数组操作了。其它的函数用于对单个元素的操作,由于使用不多,而且效率也不高,所以本文中不进行说明。
1. SafeArrayAccessData
这个函数用于获取SafeArray的数据指针,并锁定SafeArray数组的数据。在取得了数据指针之后,就可以直接访问SafeArray数组中的数据了。如果数组类型是Type,那么所取得的数据指针实际上就是Type类型的数组的地址。在多维数组的情况下,必须把多个维度的下标转换成一维下标进行访问。
2. SafeArrayUnaccessData
这个函数的作用是对SafeArray数据解锁,解锁后,就不应该继续对数据指针进行读写访问。如果要访问,必须重新获取并锁定数据。
3. 确定数组结构
在访问数组之前,必须知道数组中数据的类型,、维数以及每个维度的下界和长度。COM提供了取得这些数组参数的函数。取得类型,返回“VT_”开头的类型枚举值: HRESULT SafeArrayGetVartype ( SAFEARRAY * pSA, VARTYPE * pVarType); 取得维数,返回数组的维数: UINT SafeArrayGetDim ( SAFEARRAY * pSA); 取得每个维度的属性,返回指定维数(nDim)的上界和下界(nDim从1开始): HRESULT SafeArrayGetLBound ( SAFEARRAY * pSA, UINT nDim, long * pLBound); HRESULT SafeArrayGetUBound ( SAFEARRAY * pSA, UINT nDim, long * pUBound); 取得自定义类型接口,对于自定义结构数组,返回自定义结构类型数据的指针: HRESULT SafeArrayGetRecordInfo ( SAFEARRAY * pSA,IRecordInfo ** ppRecordInfo);
4. 访问普通一维数组
从SafeArrayAccessData返回的指针实际上就是C语言中的一维数组地址。在VC++中可以像访问普通数组一样读写这个数组。需要注意的是,在C语言中,所有的数组下标都是从0开始的。而在SafeArray中,数组下标可以从任何数字开始。所以在访问前必须进行转换。转换方法就是从SafeArray的下标中减去数组的下界,就可以得到C语言中数组的下标了。如下: Type * pData;long LBound;SafeArrayAccessData(pSA, (void HUGEP **) &pData);SafeArrayGetLBound(pSA, 1, &LBound);Type Item = pData[n – LBound];
5. 访问多维数组
访问多维数组和访问一维数组类似,只是要把多维下标转换成一维下标。把多维下标转换成一维下标的方法和在数组指针中介绍的是相似的。设:有n个维度,每个维度的长度(上界减去下界加一)分别是L1、L2、…、Ln。要转换的下标是X1、X2、…、Xn。可以根据下述公式转换成一维数组的下标。X1+X2*L1+X3*(L1*L2)+X4*(L1*L2*L3)+…+Xn*(L1*L2*…*L(n-1))
6. 访问自定义结构数组
访问自定义结构数组的时候,可以使用#iimport自动生成或者IDL编译产生的类型定义。如果没有办法取得自定义结构的声明,可以使用IRecordInfo接口中的方法间接访问自定义结构。首先需要取得自定义结构的长度,这可以通过IRecordInfo::GetSize方法取得。
学会使用SafeArray也是很重要的,因为在ADO编程中经常要用。它的主要目的是用于automation中的数组型参数的传递。因为在网络环境中,数组是不能直接传递的,而必须将其包装成SafeArray。实质上SafeArray就是将通常的数组增加一个描述符,说明其维数、长度、边界、元素类型等信息。SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。在VARIANT的vt成员的值如果包含VT_ARRAY|...,那么它所封装的就是一个SafeArray,它的parray成员即是指向SafeArray的指针。SafeArray中元素的类型可以是VARIANT能封装的任何类型,包括VARIANT类型本身。
使用SafeArray的具体步骤:
方法一:
包装一个SafeArray:
(1). 定义变量,如:
VARIANT varChunk;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
(2). 创建SafeArray描述符:
uIsRead=f.Read(bVal,ChunkSize);//read array from a file.
if(uIsRead==0)break;
rgsabound[0].cElements =uIsRead;
rgsabound[0].lLbound = 0;
psa = SafeArrayCreate(VT_UI1,1,rgsabound);
(3). 放置数据元素到SafeArray:
for(long index=0;index<uIsRead;index++)
{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))
::MessageBox(NULL,"出毛病了。","提示",MB_OK | MB_ICONWARNING);
}
一个一个地放,挺麻烦的。
(4). 封装到VARIANT内:
varChunk.vt = VT_ARRAY|VT_UI1;
varChunk.parray = psa;
这样就可以将varChunk作为参数传送出去了。
读取SafeArray中的数据的步骤:
(1). 用SafeArrayGetElement一个一个地读
BYTE buf[lIsRead];
for(long index=0;index<lIsRead;index++)
{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}
就读到缓冲区buf里了。
方法二:
使用SafeArrayAccessData直接读写SafeArray的缓冲区:
(1). 读缓冲区:
BYTE *buf;
SafeArrayAccessData(varChunk.parray, (void **)&buf);
f.Write(buf,lIsRead);
SafeArrayUnaccessData(varChunk.parray);
(2). 写缓冲区:
BYTE *buf;
::SafeArrayAccessData(psa, (void **)&buf);
for(long index=0;index<uIsRead;index++)
{
buf[index]=bVal[index];
}
::SafeArrayUnaccessData(psa);
varChunk.vt = VT_ARRAY|VT_UI1;
varChunk.parray = psa;
这种方法读写SafeArray都可以,它直接操纵SafeArray的数据缓冲区,比用SafeArrayGetElement和SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData(psa),否则会出错的
何谓SAFEARRAY:
SAFEARRAY实际上是一个结构,关于这部分可以参考MSDN。
ms-help://MS.MSDNQTR.2003FEB.2052/automat/htm/chap7_9ntx.htm
我们不需要关心16位操作系统下的定义,因为我们团队只在WIN2000以上平台下开发。
创建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);
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);
方法三:使用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之方法五
这种方式在访问多维数组的时候很有用
组件/客户中传递SAFEARRAY的原则:
1) 在堆上创建SAFEARRAY数组
2) 一方创建,一方回收
3) 接收方不可以修改SAFEARRAY的数据,只能读或者销毁
- COM中使用的数组-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray[转载/修改]
- [转]在COM中使用数组参数-SafeArray
- 在COM中使用数组参数-SafeArray[转载/修改] 2011-11-29 15:02
- 在COM中使用数组参数-SafeArray(转自http://dev.csdn.net/article/19/19696.shtm)
- 使用COM提供SafeArray数据
- SAFEARRAY(安全数组)使用详解
- SAFEARRAY的使用
- SAFEARRAY的使用
- safearray的使用
- The terminal server has exceeded the maximum number of allowed clients
- Hadoop技术内幕:深入解析MapReduce架构设计与实现原理
- Android开发---从零搞起2
- 关于工作与生活的转帖
- 返回函数指针的指针函数
- COM中使用的数组-SafeArray
- ICE Manual(Documentation for Ice 3.5)---Connection Management(Connection Establishment)
- 抢Google等巨头生意,纽约大学小伙挖掘并出售自己数据
- c# 读取摄像头录像截图
- many-to-one各属性含义
- Linux下C语言的socket函数解析
- TextView字幕效果
- 行转列
- 关于输入字符问题