在COM中使用数组参数-SafeArray(转自http://dev.csdn.net/article/19/19696.shtm)

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 方法取得。

访问自定义结构中的字段内容,通过 IRecordInfo::GetField IRecordInfo::PutField 方法实现。

通过 IRecordInfo 中的其它方法还可以取得每个字段的属性内容。大家可以参考相关文档。

1.2.3             释放SafeArray数组

释放 SafeArray 数组应该通过 COM 的支持函数:

 

HRESULT SafeArrayDestroy(SAFEARRAY * pSA);

 

1.3      使用SafeArray的IDL定义

每个接口都要通过 IDL 生成代理和占位程序代码。为了使代理和占位程序能够正确地对参数进行序列化,必须正确的书写 IDL 定义。

MIDL 工具直接支持 SafeArray 类型数据的传递。但是,在传递 SafeArray 数据的时候,必须通过 SAFEARRAY 的指针进行。困难在于, VC++ 6.0 的添加方法和添加属性的工具不能够正确的处理 SafeArray 数组的情况。

 

IDL 中,数组必须指定类型,如下:

 

[id(10)] HRESULT Foo([in] SAFEARRAY(LONG) pParam);

 

在实现的函数声明中,要使用相应的指针类型:

 

HRESULT Foo(SAFEARRAY * pParam);

 

输出和输入输出类型的数组参数,在 IDL 中必须使用指针参数,而在函数声明中则是双重指针。

 

[id(11)] HRESULT Foo2([out] SAFEARRAY(LONG) * ppParam);

 

函数声明如下:

 

HRESULT Foo2(SAFEARRAY ** ppParam);

 

1.4      VARIANT和SafeArray

VB 的接口中,经常通过 VARIANT 传递数组参数。这里简述一下使用 VARIANT 参数传递数组中需要注意的地方。

1.4.1             输入数组

对于输入数组,可以使用 VARIANT 指针,也可以使用 VARIANT 类型参数。在这两种情况下, VARIANT 中的类型是不同的。

当使用 VARIANT 指针时,输入的 VARIANT 参数的类型 (vt 参数的值 ) VT_ARRAY | VT_BYREF | VT_xxx 。此时,使用 VARIANT 参数的 pparray 字段取得 SafeArray 指针。

如果参数是 VARIANT ,输入的 VARIANT 参数的类型( vt 参数的值)是 VT_ARRAY | VT_xxx 。使用 VARIANT 参数的 parray 字段取得 SafeArray 指针。

必须注意这两种情况下, VARIANT 的类型不同,所以代码也会有区别。

1.4.2             输出数组

输出和输入输出数组,必须使用 VARIANT 指针,这时, VARIANT 类型是 VT_ARRYA | VT_BYREF | VT_xxx

1.5      SafeArray内存管理

使用 COM 专用的创建和销毁 API 函数处理 SafeArray

对于输入型的 SafeArray ,调用方负责创建和销毁 SafeArray ;对于输出型的 SafeArray ,由被调用方创建,调用方销毁;输入输出型 SafeArray ,调用方创建,被调用方可以销毁并重新创建,最终由调用方销毁。

 



[1] 这样就可以在微软修改 SafeArray 结构时,保证程序兼容性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值