VARIANT结构体主要是使用在COM(组件对象模型)中用于传递参数使用,它的存在主要是为了保持一个在COM参数传递方法的统一性,它几乎包含了所有普通常用类型的数据类型的传递,如整型,浮点型,布尔型等等,以及相应类型的指针类型,如整型指针。它的使用也比较方便。先来看看这个结构体它的结构:
typedef struct tagVARIANT {
union {
struct __tagVARIANT {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
LONGLONG llVal;
LONG lVal;
BYTE bVal;
SHORT iVal;
FLOAT fltVal;
DOUBLE dblVal;
VARIANT_BOOL boolVal;
_VARIANT_BOOL bool;
SCODE scode;
CY cyVal;
DATE date;
BSTR bstrVal;
IUnknown *punkVal;
IDispatch *pdispVal;
SAFEARRAY *parray;
BYTE *pbVal;
SHORT *piVal;
LONG *plVal;
LONGLONG *pllVal;
FLOAT *pfltVal;
DOUBLE *pdblVal;
VARIANT_BOOL *pboolVal;
_VARIANT_BOOL *pbool;
SCODE *pscode;
CY *pcyVal;
DATE *pdate;
BSTR *pbstrVal;
IUnknown **ppunkVal;
IDispatch **ppdispVal;
SAFEARRAY **pparray;
VARIANT *pvarVal;
PVOID byref;
CHAR cVal;
USHORT uiVal;
ULONG ulVal;
ULONGLONG ullVal;
INT intVal;
UINT uintVal;
DECIMAL *pdecVal;
CHAR *pcVal;
USHORT *puiVal;
ULONG *pulVal;
ULONGLONG *pullVal;
INT *pintVal;
UINT *puintVal;
struct __tagBRECORD {
PVOID pvRecord;
IRecordInfo *pRecInfo;
} __VARIANT_NAME_4;
} __VARIANT_NAME_3;
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
} VARIANT, *LPVARIANT, VARIANTARG, *LPVARIANTARG;
这个结构体呢,有5个成员,分别是 VARTYPE
vt ,WORD wReserved1,WORD wReserved2,WORD wReserved3,和最后一个共用体。其中vt用以指明最后一个共用体中哪一个成员有效,wReserved1,wReserved2,wReserved3,这三个我们使用的时候不用管,系统保留,最后一个共用体根据vt的提示,对相应的成员进行值的存储。我们从两个不同的角度来理解,首先是使用VARIANT来存储参数,首先是声明一个这个结构体的对象,然后对对象的vt进行赋值,它可接受的值是一个枚举值,也就说只能在枚举这个范围内取值,比如我要用VARIANT传递一个整数,现在我对vt的赋值为VT_INT,这样就说明了我要使用这个结构体中共用体的整型变量,接着对INT变量进行赋值,赋我们要传递的值。这样就完成VARIANT的传递。现在我们从另外一个角度来理解VARIANT,刚才是我们对VARIANT对象进行赋值传递,现在我们是这个VARIANT对象的接收者,我们从参数中获得这个对象之后,我们首先检查这个结构体的vt成员,看它哪个类型的变量有效,比如就这个例子而言,检查到vt的值是VT_INT,因此,我直接去获取这个结构体中VT_INT所对应的变量,获取它的值。这样,我们从传递到使用两个角度来理解了VARIANT结构体,概括起来说,就是vt指明了我要传递的变量的类型,结构体中共用体的成员用来存储vt指明的类型的值。
下面来看看具体VARIANT结构体是如何使用赋值的,首先是第一种方法:
首先我们声明一个VARIANT结构体的对象vr1,然后使用VariantInit函数对其进行初始化,它的作用就是对vt赋VT_EMPTY,对别的变量值附空,否则就是一个随机值,这个过程和我们声明一个int变量一样,如果声明的时候不初始化,就是一个随机值。编程过程中,不管是指针还是什么变量,都应该在声明之后对其进行初始化。接着就是我赋VT_INT给vt,这里的V_VT就是代表要对vr1结构体中的vt成员进行幅值,接着对vr1中的INT成员赋值,这里的V_INT就是表示要对INT赋值,因此这里有一个规律,就是V_,它的后面加成员类型就可以对相应的成员赋值,而这里的成员类型有一个对照表,在文章的最后给出,比如,我要对BSTR成员赋值,我就用V_BSTR。这时候,就可以使用vr1了,使用完成之后,我们应该调用VariantClear函数,这个函数的作用就是将vt赋值为VT_EMPTY,以及释放使用这个结构体中的内存中的内容,如果是com对象,该函数是不会进行对象的release操作的,不管怎么样,我们都应该在使用完了VARIANT结构体之后,调用这个函数。
下面是第二种方法,原理和过程和地中方法类似,有一点微小的区别,就是VARIANT结构体的赋值上面来说,有点不同,如下:
接下来是字符串的操作,BSTR,我们不能直接将字符串传递一个VARIANT结构体对象,而是要用到函数SysAllocString,它的返回值就是一个BSTR,由它分配一个字符串,供VARIANT,对于为什么要这么做,可以自己查看MSDN COM的Automation那部分:
完成之后,我们还应该调用SysFreeString来释放由SysAllocString分配的内存。
另外,我们可以在类型与变量的对照表中发现,同一类型,对应了两种不同的变量,如,INT对应了变量有intVal和pintVal,其实这很简单,后者是指针性,如果要使用,说明我们赋值的对象是一个指针,如下:
最后给出类型与变量的对照表,如果是使用地中方法用V_加类型,就直接使用下表中VT_后的名称就可以了:
Member name |
Description |
VT_EMPTY
|
Indicates that a value was not specified. |
VT_NULL
|
Indicates a null value, similar to a null value in SQL. |
VT_I2
|
Indicates a short
integer. |
VT_I4
|
Indicates a long
integer. |
VT_R4
|
Indicates a float
value. |
VT_R8
|
Indicates a double
value. |
VT_CY
|
Indicates a currency value. |
VT_DATE
|
Indicates a DATE value. |
VT_BSTR
|
Indicates a BSTR string. |
VT_DISPATCH
|
Indicates an IDispatch
pointer. |
VT_ERROR
|
Indicates an SCODE. |
VT_BOOL
|
Indicates a Boolean value. |
VT_VARIANT
|
Indicates a VARIANT far
pointer. |
VT_UNKNOWN
|
Indicates an IUnknown
pointer. |
VT_DECIMAL
|
Indicates a decimal
value. |
VT_I1
|
Indicates a char
value. |
VT_UI1
|
Indicates a byte
. |
VT_UI2
|
Indicates an unsigned
short
. |
VT_UI4
|
Indicates an unsigned
long
. |
VT_I8
|
Indicates a 64-bit integer. |
VT_UI8
|
Indicates an 64-bit unsigned integer. |
VT_INT
|
Indicates an integer value. |
VT_UINT
|
Indicates an unsigned
integer value. |
VT_VOID
|
Indicates a C style void
. |
VT_HRESULT
|
Indicates an HRESULT. |
VT_PTR
|
Indicates a pointer type. |
VT_SAFEARRAY
|
Indicates a SAFEARRAY. Not valid in a VARIANT. |
VT_CARRAY
|
Indicates a C style array. |
VT_USERDEFINED
|
Indicates a user defined type. |
VT_LPSTR
|
Indicates a null-terminated string. |
VT_LPWSTR
|
Indicates a wide string terminated by
null
Nothing
nullptr
a null reference (Nothing in Visual Basic)
. |
VT_RECORD
|
Indicates a user defined type. |
VT_FILETIME
|
Indicates a FILETIME value. |
VT_BLOB
|
Indicates length prefixed bytes. |
VT_STREAM
|
Indicates that the name of a stream follows. |
VT_STORAGE
|
Indicates that the name of a storage follows. |
VT_STREAMED_OBJECT
|
Indicates that a stream contains an object. |
VT_STORED_OBJECT
|
Indicates that a storage contains an object. |
VT_BLOB_OBJECT
|
Indicates that a blob contains an object. |
VT_CF
|
Indicates the clipboard format. |
VT_CLSID
|
Indicates a class ID. |
VT_VECTOR
|
Indicates a simple, counted array. |
VT_ARRAY
|
Indicates a SAFEARRAY
pointer. |
VT_BYREF
|
Indicates that a value is a reference. |
更多详细信息,请参考msdn
或是有任何编程问题,到http://www.提问或是查阅。
使用VARIANT来传递参数意味着非强类型语言(例如VBScript)能够调用使用强类型语言(C++)实现的方法。
VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义。
VARIANT 数据类型在文件OAIDL.IDL中定义如下:
- C/C++ code复制代码
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数据结构包含两个域(如果不考虑保留的域)。vt域描述了第二个域的数据类型。为了使多种类型能够在第二个域中出现,我们定义了一个联合结构。所以,第二个域的名称随着vt域中输入值的不同而改变。用于指定vt域值情况的常量在联合的定义中以每一行的注释形式给出。
使用VARIANT和VARIANTARG数据结构要分两步完全。举一个例子,让我们考虑如下代码:
long lValue = 999;
VARIANT vParam;
vParam.vt = VT_I4;
vParam.lVal = lValue;
在第一行中指定数据类型。常量VT_I4表明在第二个域中将出现一个long型的数据。根据类型VARIANT的定义,可以得知,当一个long型数据存入VARIANT类型时,其第二个域使用的名称是lVal。