MFC

SAFEARRAY的用法

Posted by Kerwen Blog on August 20, 2021

什么是SAFEARRAY呢?可以理解为一个数组,可以定义维数、长度、边界、元素类型等信息,差不多相当于C#中的List. 在编写COM组件时,需要一次传递很多的数据时,使用SAFEARRAY会很方便
首先要知道,SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。
一、要打包的数据和需要定义的变量

1
2
3
4
5
6
7
8
9
_variant_t vValue[4];
vValue[0] = _bstr_t("字符串1");
vValue[1] = _bstr_t("字符串2");
vValue[2] = _bstr_t("字符串3");
vValue[3] = _bstr_t("字符串4");
 
SAFEARRAY *psaValue;
SAFEARRAYBOUND rgsaBound[1];
VARIANT vsaValue;

SAFEARRAY就是所谓的安全数组,psaValue是一个指向SAFEARRAY的指针,

SAFEARRAYBOUND

在VC中,SAFEARRAYBOUND的定义如下:

1
2
3
4
5
6
7
typedef struct tagSAFEARRAYBOUND
{
	ULONG cElements;
	LONG lLbound;
} SAFEARRAYBOUND;
 
typedef struct tagSAFEARRAYBOUND *LPSAFEARRAYBOUND;

SAFEARRAYBOUND是一个结构体,里面有两个变量
ULONG cElements表示的是元素的数目(更准确的说是在本维中的数目)
LONG lLbound表示的是一个逻辑起点序号,实际访问内存的时候,安全数组会将程序指定的序号减去lLbound,比如你将其设置为10000, a[10000]这相当于A[0]a[999]就越界了,所以在没有特殊要求的情况下,lLbound一般为0
还有一点,定义的时候是SAFEARRAYBOUND rgsaBound[1]
rgsaBound[1]表示的是一位数组,二维数组要定义为rgsaBound[2]

VARIANT vsaValue 这个就是最终要的得到的变量了,可以把这个变量作为参数传出去。

实现代码及函数、参数意义

1
2
3
4
5
6
7
8
9
SAFEARRAY *psaValue;
psaValue = SafeArrayCreate(VT_VARIANT, 1, rgsaBound); 
for (long i = 0; i < 4; i++)
{
	SafeArrayPutElement(psaValue, &i, &vValue[i]);
}
 
vsaValue.vt = VT_ARRAY | VT_VARIANT; 
V_ARRAY(&vsaValue) = psaValue;

创建SAFEARRAY

1
psaValue = SafeArrayCreate(VT_VARIANT, 1, rgsaBound);

第一个参数VT_VARIANT表示数组的类型,第二个参数表示创建数组的维数,本例中是一维,第三个参数是对这个数组各个维度的描述。 SafeArrayCreate()就是创建SAFEARRAY的函数,准确的说是在堆中创建了一个SAFEARRAY,也就是说在这个函数里面,调用了new或者malloc()之类的申请了一个空间。

放置元素到数组中

1
2
3
4
for (long i = 0; i < 4; i++)
{
	SafeArrayPutElement(psaValue, &i, &vValue[i]);
}

第一个参数是指向SAFEARRAY的指针;第二个参数是long型指针,表示SAFEARRAY元素的下标,即唯一确定一个SAFEARRAY元素;第三个参数就是要放置的那个值的指针了。

指明vsaValue存放值得类型

1
vsaValue.vt = VT_ARRAY | VT_VARIANT;

VARIANTvt成员的值如果包含VT_ARRAY,那么它所封装的就是一个SAFEARRAY,它的parray成员即是指向SAFEARRAY的指针。 VT_ARRAY说明vsaValue封装的是一个SAFEARRAYVT_VARIANT说明SAFEARRAY的元素是VARIANT类型。

完成封装

1
V_ARRAY(&vsaValue) = psaValue; //等价于vsaValue.parray = psaValue

参考

SAFEARRAY基本用法详解
SAFEARRAY