看流星社区

 找回密码
 注册账号
查看: 2307|回复: 0

在COM中使用数组参数-SafeArray

[复制链接]

该用户从未签到

发表于 2015-3-28 16:28:41 | 显示全部楼层 |阅读模式
在COM中使用数组参数-SafeArray
关键字:DCOM、数组、自定义类型、Marshal、SafeArray、ICollection
1      使用SafeArray

SafeArray是VB中的数组存储方式。通过SafeArray,可以在VC++和VB间相互调用。SafeArray也是Automation中的标准数组存储方式。
1.1     SafeArray处理函数

COM提供了一套API用于处理SafeArray。为了保证程序和SafeArray结构无关[1],程序中建立、读取、更改和释放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
);

SafeArrayCreate于建立多维普通数组。SafeArrayCreateEx用于建立多维自定义类型或接口指针数组。SafeArrayCreateVector用于建立一维普通数组。SafeArrayCreateVectorEx用于建立一维自定义类型或接口指针数组。
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方法取得。
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-4-25 18:49

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表