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