MFC

VARIANT的用法

Posted by Kerwen Blog on September 2, 2021

VARIANT结构体主要是使用在COM(组件对象模型)中用于传递参数使用,它的存在主要是为了保持一个在COM参数传递方法的统一性,它几乎包含了所有普通常用类型的数据类型的传递,如整型,浮点型,布尔型等等,以及相应类型的指针类型,如整型指针。Variant 也可以包含 EmptyErrorNothingNull等特殊值。可以用 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的使用方法