在我写的谈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不能通过?
怎么样,棒极了吧!