在使用COM时,有时候用户不知道函数的输入参数,输出参数的具体类型。例如用户传入long类型,函数会把用户传入的long转换成自己想要的参数类型例如BSTR。其中起到关键作用的就是VARIANT类型
VARIANT.vt==当前存储的数据类型
VARIANT.(bVal,iVal,...,pvarVal,byref)==当前存储的数据
正确使用VARIANT需要注意:
需要使用VariantInit来初始化
需要使用VariantCopy来复制
需要使用VariantClear来释放资源
VARIANT只能作为单层指针
可以通过VariantChangeType[Ex]来做类型转换
The CComVariant Class
class CComVariant: public tagVARIANT { ... };
CComVariant直接继承VARIANT,表示可以直接将CComVariant代替VARIANT做参数传递
Constructors and Destructor
23种构造函数
CComVariant() { ::VariantInit(this); } ~CComVariant() { Clear(); }
CComVariant(BYTE nSrc) { vt = VT_UI1; bVal = nSrc; } CComVariant(short nSrc) { vt = VT_I2; iVal = nSrc; } CComVariant(long nSrc, VARTYPE vtSrc = VT_I4) { ATLASSERT(vtSrc == VT_I4 || vtSrc == VT_ERROR); vt = vtSrc; lVal = nSrc; } CComVariant( float fltSrc) { vt = VT_R4; fltVal = fltSrc; }
为了区分易于混淆的参数,定义了第二个参数加以区分
CComVariant(long nSrc, VARTYPE vtSrc = VT_I4) ;
VARIANT使用VARIANT_TRUE,VARIANT_FALSE来定义bool变量
CComVariant接受IDispatch*,IUnknown;并作相应的AddRef操作
CComVariant(const VARIANT& varSrc) { vt = VT_EMPTY; InternalCopy (&varSrc); } CComVariant(const CComVariant& varSrc); { /* Same as above */ }
void InternalCopy(const VARIANT* pSrc) { HRESULT hr = Copy(pSrc); if (FAILED(hr)) { vt = VT_ERROR; scode = hr; #ifndef _ATL_NO_VARIANT_THROW AtlThrow(hr); #endif } }
当Copy失败,vt=VT_ERROR,也可以定义_ATL_NO_VARIANT_THROW使其throw
要注意不要将未初始化的VARIANT作为参数传给构造函数如下代码将总是发生错误
void func () { // The following code is incorrect
VARIANT v; // Uninitialized stack garbage in vt member
CComVariant sv(v); // Indeterminate state
}
对于传入字符串,CComVariant内部总是以BSTR复制储存,只有传入CComBSTR时,才会正确处理夹在中间的Nul
Assignment
CComVariant定义了33个函数,所有都有如下步骤
释放当前资源
设置新的vt
储存数据
对于=(long*),vt=VT_I4 | VT_BYREF
对于=(const SAFEARRAY *pSrc),vt=VT_ARRAY | 元素类型
template< typename T > void SetByRef( T* pT ) { Clear(); vt = CVarTypeInfo< T >::VT|VT_BYREF; byref = pT; }
用于产生一个原始数据的引用
CVarTypeInfo在ATL8中不被使用
CComVariant Operations
HRESULT Clear() { return ::VariantClear(this); }
HRESULT Copy(const VARIANT* pSrc) { return ::VariantCopy(this, const_cast<VARIANT*>(pSrc)); } 例如:
STDMETHODIMP SomeClass::put_Option (const VARIANT* pOption) { // Option saved in member m_Option of type CComVariant return m_varOption.Copy (pOption) ; }HRESULT Detach(VARIANT* pDest);
不要在[out]中使用Detach,因为操作会对pDest先做Clear操作,而[out]参数是未分配类型的,这时将导致数据异常
STDMETHODIMP SomeClass::get_Option (VARIANT* pOption) { CComVariant varOption ; ... Initialize the variant with the output data // Wrong! The following code can generate an exception, // corrupt your heap, and give at least seven years bad luck! return varOption.Detach (pOption); }
Before detaching into an [out] VARIANT argument, be sure to initialize the output argument:
// Special care taken to initialize [out] VARIANT ::VariantInit (pOption) ; // or pOption->vt = VT_EMPTY ; return vOption.Detach (pOption); // Now we can Detach safely.
HRESULT Attach(VARIANT* pSrc);
用于转移所有权
STDMETHODIMP SomeClass::get_Option (VARIANT* pOption); void VerboseGetOption () { VARIANT v; pObj->get_Option (&v) ; CComVariant cv; cv.Attach (&v); // Destructor now releases the VARIANT }
void FragileGetOption() {
CComVariant v; // This is fragile code!!
pObj->get_Option (&v) ; // Directly update the contained
// VARIANT. Destructor now releases
// the VARIANT.
}
下述代码,因为get_Option并不会释放[out]参数原有资源
void LeakyGetOption() { CComVariant v (OLESTR ("This string leaks!")) ; pObj->get_Option (&v) ; // Directly updates the contained // VARIANT. Destructor now releases // the VARIANT. }
HRESULT ChangeType(VARTYPE vtNew, const VARIANT* pSrc = NULL);
可以转换数据类型
ComVariant Comparison Operators
bool operator==(const VARIANT& varSrc) const ; bool operator!=(const VARIANT& varSrc) const ;
当不同类型,上述操作返回false,当同一类型使用VarCmp API进行比较
bool operator<(const VARIANT& varSrc) const ; bool operator>(const VARIANT& varSrc) const ;
CComVariant Persistence Support
HRESULT WriteToStream(IStream* pStream); HRESULT ReadFromStream(IStream* pStream); ULONG GetSize() const;