ATL为了更好的让用户体验便捷并使程序的接口参数更加通用、灵活,引入了一种叫做VARIANT的数据类型,这个类型是通过一个结构体来定义的,如果不仔细阅读VARIANT结构体的定义,那么就不能体会编写者的良苦用心,更无法体会CComVariant设计的美妙,有时候用艺术的眼光欣赏程序,他们并不枯燥,相反他们真的很美。

下面是VARIANT结构体的定义,该定义在<OAIDL.h>中。

typedef struct tagVARIANT VARIANT;

struct  tagVARIANT
    {
    union
        {
        struct  __tagVARIANT
            {
            VARTYPE vt;
            WORD wReserved1;
            WORD wReserved2;
            WORD wReserved3;
            union
                {
                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 __RPC_FAR *punkVal;
                IDispatch __RPC_FAR *pdispVal;
                SAFEARRAY __RPC_FAR *parray;
                BYTE __RPC_FAR *pbVal;
                SHORT __RPC_FAR *piVal;
                LONG __RPC_FAR *plVal;
                FLOAT __RPC_FAR *pfltVal;
                DOUBLE __RPC_FAR *pdblVal;
                VARIANT_BOOL __RPC_FAR *pboolVal;
                _VARIANT_BOOL __RPC_FAR *pbool;
                SCODE __RPC_FAR *pscode;
                CY __RPC_FAR *pcyVal;
                DATE __RPC_FAR *pdate;
                BSTR __RPC_FAR *pbstrVal;
                IUnknown __RPC_FAR *__RPC_FAR *ppunkVal;
                IDispatch __RPC_FAR *__RPC_FAR *ppdispVal;
                SAFEARRAY __RPC_FAR *__RPC_FAR *pparray;
                VARIANT __RPC_FAR *pvarVal;
                PVOID byref;
                CHAR cVal;
                USHORT uiVal;
                ULONG ulVal;
                INT intVal;
                UINT uintVal;
                DECIMAL __RPC_FAR *pdecVal;
                CHAR __RPC_FAR *pcVal;
                USHORT __RPC_FAR *puiVal;
                ULONG __RPC_FAR *pulVal;
                INT __RPC_FAR *pintVal;
                UINT __RPC_FAR *puintVal;
                struct  __tagBRECORD
                    {
                    PVOID pvRecord;
                    IRecordInfo __RPC_FAR *pRecInfo;
                    } __VARIANT_NAME_4;
                } __VARIANT_NAME_3;
            } __VARIANT_NAME_2;
        DECIMAL decVal;
        } __VARIANT_NAME_1;
    };

 

这个定义意味着可以兼容所有的数据类型,但是它的缺点也是显而易见的,不宜使用,比如,我想给VARIANT类型初始化一个整数,需要以下步骤:

VARIANT varInt;

varInt.vt = VT_I2;

varInt.ival = 10;

然而,当想把VARIANT初始化为其它类型时就要通过这两步赋值了,这里不再赘述,总之要先赋值类型,再赋值数据,不仅麻烦而且易错。为了使VARIANT数据类型变得方便可靠,CComVARIANT类型被顺水推舟的做了出来,我们同样先来看看它的定义吧,真的很让人享受。

// CComVariant

class CComVariant : public tagVARIANT
{
// Constructors
public:
 CComVariant()
 {
  vt = VT_EMPTY;
 }
 ~CComVariant()
 {
  Clear();
 }

 CComVariant(const VARIANT& varSrc)
 {
  vt = VT_EMPTY;
  InternalCopy(&varSrc);
 }

 CComVariant(const CComVariant& varSrc)
 {
  vt = VT_EMPTY;
  InternalCopy(&varSrc);
 }

 CComVariant(BSTR bstrSrc)
 {
  vt = VT_EMPTY;
  *this = bstrSrc;
 }
 CComVariant(LPCOLESTR lpszSrc)
 {
  vt = VT_EMPTY;
  *this = lpszSrc;
 }

#ifndef OLE2ANSI
 CComVariant(LPCSTR lpszSrc)
 {
  vt = VT_EMPTY;
  *this = lpszSrc;
 }
#endif

 CComVariant(bool bSrc)
 {
  vt = VT_BOOL;
#pragma warning(disable: 4310) // cast truncates constant value
  boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
#pragma warning(default: 4310) // cast truncates constant value
 }

 CComVariant(int nSrc)
 {
  vt = VT_I4;
  lVal = nSrc;
 }
 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(double dblSrc)
 {
  vt = VT_R8;
  dblVal = dblSrc;
 }
 CComVariant(CY cySrc)
 {
  vt = VT_CY;
  cyVal.Hi = cySrc.Hi;
  cyVal.Lo = cySrc.Lo;
 }
 CComVariant(IDispatch* pSrc)
 {
  vt = VT_DISPATCH;
  pdispVal = pSrc;
  // Need to AddRef as VariantClear will Release
  if (pdispVal != NULL)
   pdispVal->AddRef();
 }
 CComVariant(IUnknown* pSrc)
 {
  vt = VT_UNKNOWN;
  punkVal = pSrc;
  // Need to AddRef as VariantClear will Release
  if (punkVal != NULL)
   punkVal->AddRef();
 }

// Assignment Operators
public:
 CComVariant& operator=(const CComVariant& varSrc)
 {
  InternalCopy(&varSrc);
  return *this;
 }
 CComVariant& operator=(const VARIANT& varSrc)
 {
  InternalCopy(&varSrc);
  return *this;
 }

 CComVariant& operator=(BSTR bstrSrc)
 {
  InternalClear();
  vt = VT_BSTR;
  bstrVal = ::SysAllocString(bstrSrc);
  if (bstrVal == NULL && bstrSrc != NULL)
  {
   vt = VT_ERROR;
   scode = E_OUTOFMEMORY;
  }
  return *this;
 }

 CComVariant& operator=(LPCOLESTR lpszSrc)
 {
  InternalClear();
  vt = VT_BSTR;
  bstrVal = ::SysAllocString(lpszSrc);

  if (bstrVal == NULL && lpszSrc != NULL)
  {
   vt = VT_ERROR;
   scode = E_OUTOFMEMORY;
  }
  return *this;
 }

 #ifndef OLE2ANSI
 CComVariant& operator=(LPCSTR lpszSrc)
 {
  USES_CONVERSION;
  InternalClear();
  vt = VT_BSTR;
  bstrVal = ::SysAllocString(A2COLE(lpszSrc));

  if (bstrVal == NULL && lpszSrc != NULL)
  {
   vt = VT_ERROR;
   scode = E_OUTOFMEMORY;
  }
  return *this;
 }
 #endif

 CComVariant& operator=(bool bSrc)
 {
  if (vt != VT_BOOL)
  {
   InternalClear();
   vt = VT_BOOL;
  }
 #pragma warning(disable: 4310) // cast truncates constant value
  boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
 #pragma warning(default: 4310) // cast truncates constant value
  return *this;
 }

 CComVariant& operator=(int nSrc)
 {
  if (vt != VT_I4)
  {
   InternalClear();
   vt = VT_I4;
  }
  lVal = nSrc;

  return *this;
 }

 CComVariant& operator=(BYTE nSrc)
 {
  if (vt != VT_UI1)
  {
   InternalClear();
   vt = VT_UI1;
  }
  bVal = nSrc;
  return *this;
 }

 CComVariant& operator=(short nSrc)
 {
  if (vt != VT_I2)
  {
   InternalClear();
   vt = VT_I2;
  }
  iVal = nSrc;
  return *this;
 }

 CComVariant& operator=(long nSrc)
 {
  if (vt != VT_I4)
  {
   InternalClear();
   vt = VT_I4;
  }
  lVal = nSrc;
  return *this;
 }

 CComVariant& operator=(float fltSrc)
 {
  if (vt != VT_R4)
  {
   InternalClear();
   vt = VT_R4;
  }
  fltVal = fltSrc;
  return *this;
 }

 CComVariant& operator=(double dblSrc)
 {
  if (vt != VT_R8)
  {
   InternalClear();
   vt = VT_R8;
  }
  dblVal = dblSrc;
  return *this;
 }

 CComVariant& operator=(CY cySrc)
 {
  if (vt != VT_CY)
  {
   InternalClear();
   vt = VT_CY;
  }
  cyVal.Hi = cySrc.Hi;
  cyVal.Lo = cySrc.Lo;
  return *this;
 }

 CComVariant& operator=(IDispatch* pSrc)
 {
  InternalClear();
  vt = VT_DISPATCH;
  pdispVal = pSrc;
  // Need to AddRef as VariantClear will Release
  if (pdispVal != NULL)
   pdispVal->AddRef();
  return *this;
 }

 CComVariant& operator=(IUnknown* pSrc)
 {
  InternalClear();
  vt = VT_UNKNOWN;
  punkVal = pSrc;

  // Need to AddRef as VariantClear will Release
  if (punkVal != NULL)
   punkVal->AddRef();
  return *this;
 }


// Comparison Operators
public:
 bool operator==(const VARIANT& varSrc) const
 {
  if (this == &varSrc)
   return true;

  // Variants not equal if types don't match
  if (vt != varSrc.vt)
   return false;

  // Check type specific values
  switch (vt)
  {
   case VT_EMPTY:
   case VT_NULL:
    return true;

   case VT_BOOL:
    return boolVal == varSrc.boolVal;

   case VT_UI1:
    return bVal == varSrc.bVal;

   case VT_I2:
    return iVal == varSrc.iVal;

   case VT_I4:
    return lVal == varSrc.lVal;

   case VT_R4:
    return fltVal == varSrc.fltVal;

   case VT_R8:
    return dblVal == varSrc.dblVal;

   case VT_BSTR:
    return (::SysStringByteLen(bstrVal) == ::SysStringByteLen(varSrc.bstrVal)) &&
      (::memcmp(bstrVal, varSrc.bstrVal, ::SysStringByteLen(bstrVal)) == 0);

   case VT_ERROR:
    return scode == varSrc.scode;

   case VT_DISPATCH:
    return pdispVal == varSrc.pdispVal;

   case VT_UNKNOWN:
    return punkVal == varSrc.punkVal;

   default:
    ATLASSERT(false);
    // fall through
  }

  return false;
 }
 bool operator!=(const VARIANT& varSrc) const {return !operator==(varSrc);}
 bool operator<(const VARIANT& varSrc) const {return VarCmp((VARIANT*)this, (VARIANT*)&varSrc, LOCALE_USER_DEFAULT)==VARCMP_LT;}
 bool operator>(const VARIANT& varSrc) const {return VarCmp((VARIANT*)this, (VARIANT*)&varSrc, LOCALE_USER_DEFAULT)==VARCMP_GT;}

// Operations
public:
 HRESULT Clear() { return ::VariantClear(this); }
 HRESULT Copy(const VARIANT* pSrc) { return ::VariantCopy(this, const_cast<VARIANT*>(pSrc)); }
 HRESULT Attach(VARIANT* pSrc)
 {
  // Clear out the variant
  HRESULT hr = Clear();
  if (!FAILED(hr))
  {
   // Copy the contents and give control to CComVariant
   memcpy(this, pSrc, sizeof(VARIANT));
   pSrc->vt = VT_EMPTY;
   hr = S_OK;
  }
  return hr;
 }

 HRESULT Detach(VARIANT* pDest)
 {
  // Clear out the variant
  HRESULT hr = ::VariantClear(pDest);
  if (!FAILED(hr))
  {
   // Copy the contents and remove control from CComVariant
   memcpy(pDest, this, sizeof(VARIANT));
   vt = VT_EMPTY;
   hr = S_OK;
  }
  return hr;
 }

 HRESULT ChangeType(VARTYPE vtNew, const VARIANT* pSrc = NULL)
 {
  VARIANT* pVar = const_cast<VARIANT*>(pSrc);
  // Convert in place if pSrc is NULL
  if (pVar == NULL)
   pVar = this;
  // Do nothing if doing in place convert and vts not different
  return ::VariantChangeType(this, pVar, 0, vtNew);
 }

 HRESULT WriteToStream(IStream* pStream);
 HRESULT ReadFromStream(IStream* pStream);

// Implementation
public:
 HRESULT InternalClear()
 {
  HRESULT hr = Clear();
  ATLASSERT(SUCCEEDED(hr));
  if (FAILED(hr))
  {
   vt = VT_ERROR;
   scode = hr;
  }
  return hr;
 }

 void InternalCopy(const VARIANT* pSrc)
 {
  HRESULT hr = Copy(pSrc);
  if (FAILED(hr))
  {
   vt = VT_ERROR;
   scode = hr;
  }
 }
};

inline HRESULT CComVariant::WriteToStream(IStream* pStream)
{
 HRESULT hr = pStream->Write(&vt, sizeof(VARTYPE), NULL);
 if (FAILED(hr))
  return hr;

 int cbWrite = 0;
 switch (vt)
 {
 case VT_UNKNOWN:
 case VT_DISPATCH:
  {
   CComPtr<IPersistStream> spStream;
   if (punkVal != NULL)
   {
    hr = punkVal->QueryInterface(IID_IPersistStream, (void**)&spStream);
    if (FAILED(hr))
     return hr;
   }
   if (spStream != NULL)
    return OleSaveToStream(spStream, pStream);
   else
    return WriteClassStm(pStream, CLSID_NULL);
  }
 case VT_UI1:
 case VT_I1:
  cbWrite = sizeof(BYTE);
  break;
 case VT_I2:
 case VT_UI2:
 case VT_BOOL:
  cbWrite = sizeof(short);
  break;
 case VT_I4:
 case VT_UI4:
 case VT_R4:
 case VT_INT:
 case VT_UINT:
 case VT_ERROR:
  cbWrite = sizeof(long);
  break;
 case VT_R8:
 case VT_CY:
 case VT_DATE:
  cbWrite = sizeof(double);
  break;
 default:
  break;
 }
 if (cbWrite != 0)
  return pStream->Write((void*) &bVal, cbWrite, NULL);

 CComBSTR bstrWrite;
 CComVariant varBSTR;
 if (vt != VT_BSTR)
 {
  hr = VariantChangeType(&varBSTR, this, VARIANT_NOVALUEPROP, VT_BSTR);
  if (FAILED(hr))
   return hr;
  bstrWrite = varBSTR.bstrVal;
 }
 else
  bstrWrite = bstrVal;

 return bstrWrite.WriteToStream(pStream);
}

inline HRESULT CComVariant::ReadFromStream(IStream* pStream)
{
 ATLASSERT(pStream != NULL);
 HRESULT hr;
 hr = VariantClear(this);
 if (FAILED(hr))
  return hr;
 VARTYPE vtRead;
 hr = pStream->Read(&vtRead, sizeof(VARTYPE), NULL);
 if (hr == S_FALSE)
  hr = E_FAIL;
 if (FAILED(hr))
  return hr;

 vt = vtRead;
 int cbRead = 0;
 switch (vtRead)
 {
 case VT_UNKNOWN:
 case VT_DISPATCH:
  {
   punkVal = NULL;
   hr = OleLoadFromStream(pStream,
    (vtRead == VT_UNKNOWN) ? IID_IUnknown : IID_IDispatch,
    (void**)&punkVal);
   if (hr == REGDB_E_CLASSNOTREG)
    hr = S_OK;
   return S_OK;
  }
 case VT_UI1:
 case VT_I1:
  cbRead = sizeof(BYTE);
  break;
 case VT_I2:
 case VT_UI2:
 case VT_BOOL:
  cbRead = sizeof(short);
  break;
 case VT_I4:
 case VT_UI4:
 case VT_R4:
 case VT_INT:
 case VT_UINT:
 case VT_ERROR:
  cbRead = sizeof(long);
  break;
 case VT_R8:
 case VT_CY:
 case VT_DATE:
  cbRead = sizeof(double);
  break;
 default:
  break;
 }
 if (cbRead != 0)
 {
  hr = pStream->Read((void*) &bVal, cbRead, NULL);
  if (hr == S_FALSE)
   hr = E_FAIL;
  return hr;
 }
 CComBSTR bstrRead;

 hr = bstrRead.ReadFromStream(pStream);
 if (FAILED(hr))
  return hr;
 vt = VT_BSTR;
 bstrVal = bstrRead.Detach();
 if (vtRead != VT_BSTR)
  hr = ChangeType(vtRead);
 return hr;
}

 

在CComVariant的定义中,提供了几乎对于所有数据类型的构造函数,这就意味着,我们初始化CComVariant的对象时,可以简单的采用

 

CComVARIANT comVariant("ABC"); 

 

来初始化字符串,其他的数据类型的初始化也是如此简单,不仅如此,CComVariant类提供了所有类型的赋值运算符重载,这就意味着,你可以通过等号来给CComVARIANT类型的对象重新赋值,比如,你可以采用下面的方式给comVariant重新赋值,

 

comVariant = "XYZ";

 

此时,你可能在感慨CComVariant好用的同时,有一些疑惑,那就是,对于ATL中用到的函数来说,它们的涉及到VARIANT类型的参数都要求传入VARIANT * 类型,你可能在想这是为了传参效率高,然而ATL的设计者的想法远没有这么简单,起码设计者考虑了参数的通用性,要想清楚这个问题从下面几个步骤来仔细思考:

1. VARIANT数据类型不是简单的基本内置类型,比如:int char long等,这就意味着VARIANT是通过struct定义的,在C++中struct具备和class几乎同等的功能,也可以这么来讲,VARIANT也是一个类,在它的定义中有以下语句:

typedef struct tagVARIANT VARIANT;

而CComVARIANT的定义是:

class CComVariant : public tagVARIANT

这就意味着CComVariant类是VARIANT的子类,在参数传递的过程中,如果要求传入的是某个类的指针类型,那么这个类指针或者这个类的继承链中的任何一个子类的指针传入进去后都是合法的。那么,这就说所要要求传入VARIANT * 数据类型的地方都可以用CComVariant * 的类型来代替!

据此,我们可以得出一个结论,在ATL中,CComVariant完全可以取代VARIANT,而且更加方便可靠!