VARIANT
1.定义
VARIANT,变体数据类型,在文件OAIDL.IDL中定义
VARIANT数据结构包含两个域(如果不考虑保留的域)。
vt域描述了第二个域的数据类型。
为了使多种类型能够在第二个域中出现,我们定义了一个联合结构。
所以,第二个域的名称随着vt域中输入值的不同而改变。
简单来说VARIANT主要由一个变量类型vt(枚举值)和一个联合体(变量内容)组成。
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;
};
2.实例
long lValue = 999;
VARIANT vParam;
vParam.vt = VT_I4;//指定数据类型,常量VT_I4表明在第二个域中将出现一个long型的数据
vParam.lVal = lValue;//一个long型数据存入VARIANT类型时,其第二个域使用的名称是lVal
3.作用
VARIANT类型,可以跨语言(VB, C#, Java, C++)来表示任意一种类型。和BSTR一样,它也是微软约定好的一种协议。遵循这种协议,我们的变量可以正确地被支持COM的语言所认识。
4.API
VariantInit(VARIANTARG* pvarg)
初始化一个VARIANT(VARIANTARG是VARIANT别名)变量,它将变量初始化为VT_EMPTY
VariantClear(VARIANTARG * pvarg)
清理一个VARIANT对象。当调用完VariantClear后,变量的vt将会被设置为VT_EMPTY。
VariantCopy(VARIANTARG *pvargDest, const VARIANTARG *pvargSrc)
将pvargSrc中的内容拷贝到pvargDest中。如果pvargDest事先有内容,那么则使用VariantClear来释放它。
VARIANT a, b;
VariantInit(&a);
VariantInit(&b);
VariantCopy(&a, &b); // 释放a中的内容,然后a<-b
VariantClear(&a);
VariantClear(&b);
CComVariant
1.定义
类(class),在ATL中定义,是直接继承于VARIANT类的,所以它可以直接使用VARIANT成员。
头文件 :atlcomcli.h
析构函数:调用Clear()方法,也就是调用VariantClear()。
构造函数:
第一类是没有参数的构造函数,简单地调用了VariantInit。
第二类是传入值变量的构造函数,例如传入BYTE, short等,vt将分别设置为VT_UI1, VT_I2,且设置到对应的成员中。
第三类转调了自身的operator=
可以将CComVariant看为“智能VARIANT”,我们只需要给它传入适当的值,它自己来处理VARIANT的生命周期,并且它符合我们绝大部分需求
2.实现
(1) 构造
默认构造函数:CComVariant()
BYTE构造函数:CComVariant(BYTE nSrc)
short构造函数:CComVariant(short nSrc)
long 构造函数:CComVariant(long nSrc, VARTYPE=VT_I4)
bool 构造函数:CComVariant(bool nSrc)
这个函数有点特别,如果是true,那么boolVal会被赋值为ATL_VARIANT_TRUE,否则, 会被赋值为ATL_VARIANT_FALSE
IDispatch* 构造函数: CComVariant(IDispatch *)
IUnknown*构造函数: CComVariant(IUnknown *)
VARIANT构造函数: CComVariant(const VARIANT *var)
本身构造函数: CComVariant(const CComVariant & varSrc)
在使用CComVariant(const VARIANT *var)的时候,要注意一定要先初始化var参数,否则会导致 CComVariant被创建为 VT_ERROR
比如下面的代码, 导致 CComVariant被创建为 VT_ERROR
void fuck()
{
VARIANT vt;
CComVariant ccv(&vt);
}
跟BSTR相关的构造函数:
CComVariant(const CComBSTR & bstrSrc)
CComVariant(LPCOLESTR lpszSrc)
CComVariant(LPCSTR lpszSrc)
这三个构造函数,如果不能顺利的构建BSTR内存,那么也会将CComVariant构建为VT_ERROR.
上面三个构造函数,只有第一个能够正确的处理含有NULL的字符串,下面的两个将被NULL给截断,原因就是BSTR的构建过程中,会将NULL当做结束符。
(2)赋值
赋值跟构造函数类似,也是很多类似的赋值函数。
(3)Clear函数
如果要释放CComVariant,那么需要调用Clear函数,但是在CComVariant的析构函数中,Clear会被自动的调用,所以一般不需要自己去手动调用Clear函数。
(4)Copy函数
Copy将CComVariant的内容,拷贝到一个VARIANT
VARIANT vt;
long i=5;
CComVariant cvt(i);
cvt.Copy(&vt);
(5) Detach函数
如果想把CComVariant的实例内容的管理权交给VARIANT管理时,使用Detach函数。这个时候,CComVariant将不再自动管理内容。
在一个方法里面,如果要用Detach设置一个VARIANT的输出参数时候,记得一定要初始化VARIANT参数,因为在Detach的时候,CComVariant会去销毁清除指定的VARIANT,这个时候,如果VARIANT没有被初始化,那么就很可能会抛出异常。
void getVariant(VARIANT * poutVar)
{
VariantInit(poutVar);//记得一定要初始化VARIANT参数
//或者
poutVar->vt=VT_EMPTY;
//假定ccvar是一个被正确初始化和赋值的CComVariant
ccvar->Detach(poutVar);
}
(6) Attach函数
Attach可以让一个CComVariant获得VARIANT的所以权,全面接管所有权。
VARIANT vt;
vt.vt=VT_BSTR;
vt.bstrVal=::SysAllocString("Hello VARIANT");
CComVariant ccv;
ccv.Attach(&vt);
这样,CComVariant就接管了 bstr,而 vt不再拥有bstr了,可以不管vt了。
最后,给一个COM方法传递VARIANT,以及从一个方法获得VARIANT
//设置
void SetVar(VARIANT * var)
{
CComVariant ccc;
ccc=var;
//这里利用的是 VARIANT赋值函数
}
//获得
void GetVar(VARIANT * var)
{
VariantInit(var);
CComVariant ccc;
ccc.Copy(var);
//或者
ccc.Detach(var);
}
CComVariant::Attach | 将 VARIANT 附加到 CComVariant 对象。 |
CComVariant::ChangeType | 将 对象 CComVariant 转换为新类型。 |
CComVariant::Clear | 清除 CComVariant 对象。 |
CComVariant::Copy | 将 VARIANT 复制到 CComVariant 对象。 |
CComVariant::CopyTo | 复制 对象 CComVariant 的内容。 |
CComVariant::Detach | 从 对象 VARIANT 分离基础 CComVariant 。 |
CComVariant::GetSize | 返回对象内容的大小(以字节 CComVariant 数为单位)。 |
CComVariant::ReadFromStream | 从流中加载 VARIANT 。 |
CComVariant::SetByRef | 初始化 对象 CComVariant ,将 vt 成员设置为 VT_BYREF 。 |
CComVariant::WriteToStream | 将基础 VARIANT 保存到流。 |