VARIANT
结构体主要是使用在COM
(组件对象模型)中用于传递参数使用,它的存在主要是为了保持一个在COM参数传递方法的统一性,它几乎包含了所有普通常用类型的数据类型的传递,如整型,浮点型,布尔型等等,以及相应类型的指针类型,如整型指针。Variant
也可以包含 Empty
、Error
、Nothing
及 Null
等特殊值。可以用 VarType
函数或 TypeName
函数来决定如何处理 Variant
中的数据。
Variant
能够在运行期间动态的改变类型。变体类型能支持所有简单的数据类型,如整型、浮点、字符串、布尔型、日期时间、货币及OLE自动化对象等,不能够表达Object Pascal
对象。
VARIANT
数据类型在文件OAIDL.IDL
中定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct tagVARIANT {
union {
struct __tagVARIANT {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
ULONGLONG ullVal; /* VT_UI8 */
LONGLONG llVal; /* VT_I8 */
LONG lVal; /* VT_I4 */
BYTE bVal; /* VT_UI1 */
SHORT iVal; /* VT_I2 */
FLOAT fltVal; /* VT_R4 */
DOUBLE dblVal; /* VT_R8 */
VARIANT_BOOL boolVal; /* VT_BOOL */
_VARIANT_BOOL bool; /* (obsolete) */
SCODE scode; /* VT_ERROR */
CY cyVal; /* VT_CY */
DATE date; /* VT_DATE */
BSTR bstrVal; /* VT_BSTR */
IUnknown * punkVal; /* VT_UNKNOWN */
IDispatch * pdispVal; /* VT_DISPATCH */
SAFEARRAY * parray; /* VT_ARRAY */
BYTE * pbVal; /* VT_BYREF|VT_UI1 */
SHORT * piVal; /* VT_BYREF|VT_I2 */
LONG * plVal; /* VT_BYREF|VT_I4 */
LONGLONG * pllVal; /* VT_BYREF|VT_I8 */
FLOAT * pfltVal; /* VT_BYREF|VT_R4 */
DOUBLE * pdblVal; /* VT_BYREF|VT_R8 */
VARIANT_BOOL *pboolVal; /* VT_BYREF|VT_BOOL */
_VARIANT_BOOL *pbool; /* (obsolete) */
SCODE * pscode; /* VT_BYREF|VT_ERROR */
CY * pcyVal; /* VT_BYREF|VT_CY */
DATE * pdate; /* VT_BYREF|VT_DATE */
BSTR * pbstrVal; /* VT_BYREF|VT_BSTR */
IUnknown ** ppunkVal; /* VT_BYREF|VT_UNKNOWN */
IDispatch ** ppdispVal; /* VT_BYREF|VT_DISPATCH */
SAFEARRAY ** pparray; /* VT_BYREF|VT_ARRAY */
VARIANT * pvarVal; /* VT_BYREF|VT_VARIANT */
PVOID byref; /* Generic ByRef */
CHAR cVal; /* VT_I1 */
USHORT uiVal; /* VT_UI2 */
ULONG ulVal; /* VT_UI4 */
INT intVal; /* VT_INT */
UINT uintVal; /* VT_UINT */
DECIMAL * pdecVal; /* VT_BYREF|VT_DECIMAL */
CHAR * pcVal; /* VT_BYREF|VT_I1 */
USHORT * puiVal; /* VT_BYREF|VT_UI2 */
ULONG * pulVal; /* VT_BYREF|VT_UI4 */
ULONGLONG * pullVal; /* VT_BYREF|VT_UI8 */
INT * pintVal; /* VT_BYREF|VT_INT */
UINT * puintVal; /* VT_BYREF|VT_UINT */
struct __tagBRECORD {
PVOID pvRecord;
IRecordInfo * pRecInfo;
} __VARIANT_NAME_4; /* VT_RECORD */
} __VARIANT_NAME_3;
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
};
VARIANT
数据结构有5个成员,分别是VARTYPE vt
,WORD wReserved1
,WORD wReserved2
,WORD wReserved3
,和最后一个共用体。其中vt
描述了第二个域的数据类型, 用以指明最后一个共用体中哪一个成员有效,wReserved1
,wReserved2
,wReserved3
这三个为系统保留,最后一个共用体根据vt
的提示,对相应的成员进行值的存储。所以,第二个域的名称随着vt
域中输入值的不同而改变。用于指定vt
域值情况的常量在联合的定义中以每一行的注释形式给出。
可以从两个不同的角度来理解:
首先是使用VARIANT
来存储参数,首先是声明一个这个结构体的对象,然后对对象的vt
进行赋值,它可接受的值是一个枚举值,也就说只能在枚举这个范围内取值,比如我要用VARIANT传递一个整数,现在我对vt的赋值为VT_INT
,这样就说明了我要使用这个结构体中共用体的整型变量,接着对INT
变量进行赋值,赋我们要传递的值。这样就完成VARIANT
的传递。
从另外一个角度来理解VARIANT
,刚才是我们对VARIANT
对象进行赋值传递,现在我们是这个VARIANT
对象的接收者,我们从参数中获得这个对象之后,我们首先检查这个结构体的vt
成员,看它哪个类型的变量有效,比如就这个例子而言,检查到vt
的值是VT_INT
,因此,我直接去获取这个结构体中VT_INT
所对应的变量,获取它的值。这样,我们从传递到使用两个角度来理解了VARIANT
结构体,概括起来说,就是vt
指明了我要传递的变量的类型,结构体中共用体的成员用来存储vt
指明的类型的值。
1
2
3
4
long lValue = 999;
VARIANT vParam;
vParam.vt = VT_I4;
vParam.lVal = lValue;
在第一行中指定数据类型。常量VT_I4
表明在第二个域中将出现一个long
型的数据。根据类型VARIANT
的定义,可以得知,当一个long
型数据存入VARIANT
类型时,其第二个域使用的名称是lVal
。
和SafeArray
一块使用的完整示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. 定义变量
int uIsRead = 10;
DOUBLE bVal[] = {0,1,2,3,4,5,6,7,8,9};
// 2. 创建SafeArray描述符:
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];// = {10,1};
rgsabound[0].cElements =uIsRead;
rgsabound[0].lLbound = 0;
psa = SafeArrayCreate(VT_R8,1,rgsabound);
// 3. 放置数据元素到SafeArray:
for(long index=0;index<=uIsRead;index++)
{
SafeArrayPutElement(psa,&index,&bVal[index ]);
}
// 4. 封装到VARIANT内:
varChunk->vt = VT_R8;
varChunk->parray = psa;
// 5. 读取SafeArray中的数据的步骤:
DOUBLE buf[10];
for(long ind=0;ind<10;ind++)
{
::SafeArrayGetElement(pvar.parray,&ind,buf+ind);
}
double dou = 0;
for (long i = 0;i< 10;i++)
{
dou = buf[i];
}
参考
C++变体数据类型—— VARIANT
VARIANT的使用方法