在我写的谈ATL(四)--VARIANT和CComVariant中详细分析了VARAINT类型的本质,并详细说明了CComVariant为什么可以完全替代VARAINT的理由,下面我打算把BSTR和CComBSTR也详细的说明一下,不过与VARAINT和CComVariant的关系不同的是,CComVariant是VARAINT的子类,在传递参数时,利用的是子类对象is-a父类的概念。BSTR和CComBSTR是不是也是这种关系呢?不是的!我们先来看看BSTR的定义:

typedef OLECHAR __RPC_FAR *BSTR;

typedef WCHAR OLECHAR;

typedef wchar_t WCHAR;

typedef unsigned short wchar_t;

通过这一系列的宏定义,可以清楚的看出BSTR的本质,可见它并不是结构体,更不是类,而是一个穿了几层马甲的内置数据类型,从这我们也可以断言CComBSTR类肯定不是BSTR的子类,那CComBSTR怎么能做到完美的替代BSTR呢?我们还是先看看CComBSTR的定义吧,说实话,这些代码真的像艺术品一样。

// CComBSTR

class CComBSTR
{
public:
 BSTR m_str;
 CComBSTR()
 {
  m_str = NULL;
 }
  CComBSTR(int nSize)
 {
  m_str = ::SysAllocStringLen(NULL, nSize);
 }
  CComBSTR(int nSize, LPCOLESTR sz)
 {
  m_str = ::SysAllocStringLen(sz, nSize);
 }
  CComBSTR(LPCOLESTR pSrc)
 {
  m_str = ::SysAllocString(pSrc);
 }
  CComBSTR(const CComBSTR& src)
 {
  m_str = src.Copy();
 }
  CComBSTR(REFGUID src)
 {
  LPOLESTR szGuid;
  StringFromCLSID(src, &szGuid);
  m_str = ::SysAllocString(szGuid);
  CoTaskMemFree(szGuid);
 }
 CComBSTR& operator=(const CComBSTR& src)
 {
  if (m_str != src.m_str)
  {
   if (m_str)
    ::SysFreeString(m_str);
   m_str = src.Copy();
  }
  return *this;
 }

 CComBSTR& operator=(LPCOLESTR pSrc)
 {
  ::SysFreeString(m_str);
  m_str = ::SysAllocString(pSrc);
  return *this;
 }

 ~CComBSTR()
 {
  ::SysFreeString(m_str);
 }
 unsigned int Length() const
 {
  return (m_str == NULL)? 0 : SysStringLen(m_str);
 }
 operator BSTR() const
 {
  return m_str;
 }
 BSTR* operator&()
 {
  return &m_str;
 }
 BSTR Copy() const
 {
  return ::SysAllocStringLen(m_str, ::SysStringLen(m_str));
 }
 HRESULT CopyTo(BSTR* pbstr)
 {
  ATLASSERT(pbstr != NULL);
  if (pbstr == NULL)
   return E_POINTER;
  *pbstr = ::SysAllocStringLen(m_str, ::SysStringLen(m_str));
  if (*pbstr == NULL)
   return E_OUTOFMEMORY;
  return S_OK;
 }
 void Attach(BSTR src)
 {
  ATLASSERT(m_str == NULL);
  m_str = src;
 }
 BSTR Detach()
 {
  BSTR s = m_str;
  m_str = NULL;
  return s;
 }
 void Empty()
 {
  ::SysFreeString(m_str);
  m_str = NULL;
 }
 bool operator!() const
 {
  return (m_str == NULL);
 }
 HRESULT Append(const CComBSTR& bstrSrc)
 {
  return Append(bstrSrc.m_str, SysStringLen(bstrSrc.m_str));
 }
 HRESULT Append(LPCOLESTR lpsz)
 {
  return Append(lpsz, ocslen(lpsz));
 }
 // a BSTR is just a LPCOLESTR so we need a special version to signify
 // that we are appending a BSTR
 HRESULT AppendBSTR(BSTR p)
 {
  return Append(p, SysStringLen(p));
 }
 HRESULT Append(LPCOLESTR lpsz, int nLen)
 {
  int n1 = Length();
  BSTR b;
  b = ::SysAllocStringLen(NULL, n1+nLen);
  if (b == NULL)
   return E_OUTOFMEMORY;
  memcpy(b, m_str, n1*sizeof(OLECHAR));
  memcpy(b+n1, lpsz, nLen*sizeof(OLECHAR));
  b[n1+nLen] = NULL;
  SysFreeString(m_str);
  m_str = b;
  return S_OK;
 }
 HRESULT ToLower()
 {
  USES_CONVERSION;
  if (m_str != NULL)
  {
   LPTSTR psz = CharLower(OLE2T(m_str));
   if (psz == NULL)
    return E_OUTOFMEMORY;
   BSTR b = T2BSTR(psz);
   if (psz == NULL)
    return E_OUTOFMEMORY;
   SysFreeString(m_str);
   m_str = b;
  }
  return S_OK;
 }
 HRESULT ToUpper()
 {
  USES_CONVERSION;
  if (m_str != NULL)
  {
   LPTSTR psz = CharUpper(OLE2T(m_str));
   if (psz == NULL)
    return E_OUTOFMEMORY;
   BSTR b = T2BSTR(psz);
   if (psz == NULL)
    return E_OUTOFMEMORY;
   SysFreeString(m_str);
   m_str = b;
  }
  return S_OK;
 }
 bool LoadString(HINSTANCE hInst, UINT nID)
 {
  USES_CONVERSION;
  TCHAR sz[512];
  UINT nLen = ::LoadString(hInst, nID, sz, 512);
  ATLASSERT(nLen < 511);
  SysFreeString(m_str);
  m_str = (nLen != 0) ? SysAllocString(T2OLE(sz)) : NULL;
  return (nLen != 0);
 }
 bool LoadString(UINT nID)
 {
  return LoadString(_pModule->m_hInstResource, nID);
 }

 CComBSTR& operator+=(const CComBSTR& bstrSrc)
 {
  AppendBSTR(bstrSrc.m_str);
  return *this;
 }
 bool operator<(BSTR bstrSrc) const
 {
  if (bstrSrc == NULL && m_str == NULL)
   return false;
  if (bstrSrc != NULL && m_str != NULL)
   return wcscmp(m_str, bstrSrc) < 0;
  return m_str == NULL;
 }
 bool operator==(BSTR bstrSrc) const
 {
  if (bstrSrc == NULL && m_str == NULL)
   return true;
  if (bstrSrc != NULL && m_str != NULL)
   return wcscmp(m_str, bstrSrc) == 0;
  return false;
 }
 bool operator<(LPCSTR pszSrc) const
 {
  if (pszSrc == NULL && m_str == NULL)
   return false;
  USES_CONVERSION;
  if (pszSrc != NULL && m_str != NULL)
   return wcscmp(m_str, A2W(pszSrc)) < 0;
  return m_str == NULL;
 }
 bool operator==(LPCSTR pszSrc) const
 {
  if (pszSrc == NULL && m_str == NULL)
   return true;
  USES_CONVERSION;
  if (pszSrc != NULL && m_str != NULL)
   return wcscmp(m_str, A2W(pszSrc)) == 0;
  return false;
 }
#ifndef OLE2ANSI
 CComBSTR(LPCSTR pSrc)
 {
  m_str = A2WBSTR(pSrc);
 }

 CComBSTR(int nSize, LPCSTR sz)
 {
  m_str = A2WBSTR(sz, nSize);
 }

 void Append(LPCSTR lpsz)
 {
  USES_CONVERSION;
  LPCOLESTR lpo = A2COLE(lpsz);
  Append(lpo, ocslen(lpo));
 }

 CComBSTR& operator=(LPCSTR pSrc)
 {
  ::SysFreeString(m_str);
  m_str = A2WBSTR(pSrc);
  return *this;
 }
#endif
 HRESULT WriteToStream(IStream* pStream)
 {
  ATLASSERT(pStream != NULL);
  ULONG cb;
  ULONG cbStrLen = m_str ? SysStringByteLen(m_str)+sizeof(OLECHAR) : 0;
  HRESULT hr = pStream->Write((void*) &cbStrLen, sizeof(cbStrLen), &cb);
  if (FAILED(hr))
   return hr;
  return cbStrLen ? pStream->Write((void*) m_str, cbStrLen, &cb) : S_OK;
 }
 HRESULT ReadFromStream(IStream* pStream)
 {
  ATLASSERT(pStream != NULL);
  ATLASSERT(m_str == NULL); // should be empty
  ULONG cbStrLen = 0;
  HRESULT hr = pStream->Read((void*) &cbStrLen, sizeof(cbStrLen), NULL);
  if ((hr == S_OK) && (cbStrLen != 0))
  {
   //subtract size for terminating NULL which we wrote out
   //since SysAllocStringByteLen overallocates for the NULL
   m_str = SysAllocStringByteLen(NULL, cbStrLen-sizeof(OLECHAR));
   if (m_str == NULL)
    hr = E_OUTOFMEMORY;
   else
    hr = pStream->Read((void*) m_str, cbStrLen, NULL);
  }
  if (hr == S_FALSE)
   hr = E_FAIL;
  return hr;
 }
};

 

我把需要注意的地方都用红色粗体标注了,通过CComBSTR的定义我们可以看到,CComBSTR是一个顶层类,没有复杂的社会背景(比如一大堆的继承和预编译指令等),构造函数和赋值运算符提供了很多,大致可以满足初始化和赋值操作的使用,最到位的服务是我用红色粗体标注出来的两个函数,一个是强制类型转换,通过类似运算符重载的方式完成,这种语法一般的初学者比较陌生,建议再深入学习;一个是对 & 符号的运算符重载。这两个函数可以起到操作CComBSTR类完全和操作BSTR一样的感觉,也就是说你可以这样,假设要调用一个形如下面这个样子的函数:

void  TEST(BSTR str1, BSTR *pstr2, BSTR str3);

假设你定义了这样的变量:

BSTR bstr;

CComBSTR coBstr1, coBstr2;

那么调用TEST函数时就完全可以把coBstr1和coBstr2当做BSTR类型的变量来使用,如下:

TEST(bstr, &coBstr1, coBstr2);   //该调用在VC6.0不能通过?

怎么样,棒极了吧!