- 注册时间
- 2011-3-6
- 最后登录
- 1970-1-1
该用户从未签到
|
1.1
Programming with CComBSTR
1.1.1
概述CComBSTR是ATL提供的BSTR包装类,是VC 6中提供的最完善的BSTR wrapper。就像MFC CString提供了对TCHAR的封装,CComBSTR提供了对BSTR的封装。Table 1 CComBSTR Methods列出了CComBSTR的主要方法。
Table 1CComBSTR Methods
CComBSTR Method | Description | CComBSTR | 多个版本的构造函数用来创建新的BSTR。可以使用的参数包括LPCOLESTR, LPCSTR, CComBSTR。 | ~CComBSTR, Empty | 释放内部封装的BSTR. | Attach, Detach, Copy | Attach把一个已经存在BSTR加入类中。Detach把劣种的BSTR剥离,以便在超出作用域的时候,析构函数不会释放BSTR。Detach用于把CComBSTR 赋给[out]参数。
Copy用于产生一个BSTR的副本。一般用于用于把CComBSTR内容赋给[out]参数。
| operator BSTR, operator& | 允许直接操作内部的BSTR。operator BSTR用于把CComBSTR传给BSTR输入[in]参数。operator&用于把CComBSTR传给BSTR*类型输出[out]参数。 | operator=, operator+=, operator<, operator==, operator> | 重载运算符,用于赋值、字符串连接、简单比较。 | Append, AppendBSTR | 字符串连接 | Length | 计算字符串长度 | LoadString | 利用字符串资源初始化BSTR。 | ToLower, ToUpper | 字符串大小写转换。 | WriteToStream,ReadFromStream | 从IStream中读/写BSTR。 |
下面的伪代码展示了CComBSTR的典型用法:
HRESULT CMyObject::MyMethod(IOtherObject* pSomething)
{
CComBSTR bstrText(L"Hello");
bstrText += " again"; // LPCSTR conversion
bstrText.ToUpper();
pSomething->Display(bstrText); // [in] parameter
MessageBoxW(0, bstrText, L"Test", MB_OK); // Assumes Windows NT
}
对于熟悉MFC的程序员,CComBSTR让人失望。很多CString提供的方便的特性CComBSTR都没有提供。重要的缺省列在了 Table 2 Notable CComBSTR Omissions中。简而言之,CComBSTR 没有提供完整的字符串操作。它的主要用途是把LPCTSTR转换成 BSTR,同时提供一个操作BSTR的类,使程序员可以不使用COM SysXXXXString APIs 。如果需要使用复杂的字符串操作,可以使用STL提供的wstring 类。
Table 2 Notable CComBSTR Omissions
Features Not Included in CComBSTR | Explanation | LPCSTR extraction | CComBSTR 可以把一个单字节字符串转换成BSTR,但是没有提供反向转换的功能。_bstr_t 提供了LPCTSTR operator。 | String manipulation (including Replace, Insert, Delete, Remove, Find, Mid, Left, Right, and so on) | CComBSTR没有提供这些方法。如果需要,可以使用STL中的wstring。 | Language-sensitive collation | CComBSTR 提供的字符串比较(<, >, ==)按照是byte-by-byte方式进行的。没有提供语言相关的比较(language-specific collation)。如果需要可以使用wstring. | 1.1.2
CComBSTR注意事项
使用CComBSTR时需要考虑的问题。
·
CComBSTR初始化
CComBSTR 提供了一个长度初始化函数,CComBSTR(int nSize)。所以简单给CComBSTR初始化成NULL会发生意想不到的调用。
// CComBSTR(int nSize) is called。
CComBSTR bstr1 = NULL;
CComBSTR bstr2(NULL);
// CComBSTR(LPCOLESTR pSrc) is called.
CComBSTR bstr3 = static_cast< LPCOLESTR>(NULL);
CComBSTR bstr4(static_cast< LPCOLESTR>(NULL));
上面的例子中,bstr1/bstr2被初始化成长度为0的BSTR,也就是说CComBSTR::m_str是有内容的。bstr3/bstr4的值被初始化成NULL,也就是说CComBSTR::m_str == 0。这样,bstr1/bstr2在被赋新的值前需要考虑是否需要释放其中的BSTR。
·
字符集转换
尽管某些CComBSTR方法可以自动把ANSI字符串转换成Unicode。所有的接口返回的都是Unicode字符串。如果需要转回 ANSI,可以使用ATL或MFC转换类,或者Windows API。如果使用文字串修改CComBSTR,使用宽字节字符串。可以减少不必要的转换。例如:
// Declare a CComBSTR object. Although the argument is ANSI,
// the constructor converts it into UNICODE.
CComBSTR bstrMyString( "Hello World" );
// Convert the string into an ANSI string
CW2CT szMyString( bstrMyString );
// Display the ANSI string
MessageBox( NULL, szMyString, _T("String Test"), MB_OK );
// The following converts the ANSI string to Unicode
CComBSTR bstr("Test");
// The following uses a Unicode string at compile time
CComBSTR bstr(L"Test");
·
变量作用域(Scope)
象所有设计完整的类一样,CComBSTR会在离开作用域的时候释放资源。如果一个函数返回一个指向CComBSTR 的指针,可能会带来问题:指针有可能指向已经被释放的内存。此时应该使用Copy或Detach方法。参考下面的例子。
HRESULT CMyObject::MyMethod3(/*[out, retval]*/ BSTR* pbstr)
{
CComBSTR bstrText(L"Hello");
bstrText += " again";
*pbstr = bstrText; // No! Call Detach instead!
}
通过复制语句*pbstr = bstrText,被bstrText封装的BSTR的指针作为传出[out]参数传递。在MyMethod3 return时,bstrText离开作用域,CComBSTR destructor毁掉用SysFreeString释放这个BSTR。因此,调用者得到了一个指向已经被释放的内存的指针,可能导致意想不到的结果。因为bstrText即将超出作用域,所以必须使用CComBSTR Copy或 Detach给*pbstr赋值。CComBSTR Copy生成字符串的一格副本,Detach简单的把BSTR移出包装类。这样,在bstrText离开作用域的时候就不会被释放。
HRESULT CMyObject::MyMethod4(/*[out, retval]*/ BSTR* pbstr)
{
CComBSTR bstrText(L"Hello");
bstrText += L" again";
//*pbstr = bstrText.Copy(); // Better!
*pbstr = bstrText.Detach(); // Much better!
}
在这个例子中,从效率考虑,最好使用Detach而不是Copy。Detach 不需要产生一个额外副本的开销。当CComBSTR 必须在复制之后保持自己的内容的时候,例如CComBSTR是一个成员变量,必须使用Copy。
·
显式释放CComBSTR内容
程序员可以在CComBSTR 超出作用域范围前显示释放CComBSTR 中的字符串。一旦释放了,CComBSTR 内容就无效了。CComBSTR 提供了operator BSTR,所以代码中可以显示的释放其中的BSTR。
HRESULT CMyObject::MyMethod1()
{
CComBSTR bstrText(L"This is a test");
::SysFreeString(bstrText);
// The string will be freed a second time
// when the CComBSTR object goes out of scope,
// which is invalid.
// CComBSTR::Empty() should be used in order to
// explicitly free the BSTR
}
在这段代码中,bstrText 中的BSTR被释放了。但是,bstrText 仍然没有超出作用域,看起来仍然可以使用。当bstrText 最终超出作用域的时候,SysFreeString 被第二次调用。为了防止这种意外,需要把operator BSTR 从类中删除。但这样没有办法把它用于需要BSTR类型输入[in]参数的地方,会使CComBSTR 几乎没有任何用处。
·
外部CComBSTR用作[out]参数
把一个已经初始化好的CComBSTR 的地址传给一个函数作为[out]参数会导致内存泄漏。当把CComBSTR用于BSTR*类型的传出参数[out]时,必须首先调用Empty方法清空字符串的内容。
HRESULT CMyObject::MyMethod2(ISomething* p)
{
CComBSTR bstrText;
bstrText = L"Some assignment"; // BSTR is allocated.
bstrText.Empty(); // Must call empty before
pSomething->GetText(&bstrText); // using as an [out] parameter.
if(bstrText != L"Schaller")
bstrText += "Hello"; // Convert from LPCSTR.
}
在把CComBSTR作为[out]参数传递前,调用Empty释必须的。因为按照COM标准中的[out]参数的使用规则-被调用方法不应该在覆盖BSTR的内容前调用SysFreeString。如果你忘记调用Empty,调用前BSTR的内容占用的资源就会泄漏。
对于相同的代码,如果参数类型是[in, out],就不会有泄漏。因为函数会在复制之前,Free原有的串。
·
用CComBSTR给BSTR变量赋值
在下面的代码中,CStringTest 使用CComBSTR 作为成员变量保存BSTR属性。
class CStringTest
{
CComBSTR m_bstrText;
// IStringTest
public:
STDMETHOD(put_Text)(/*[in]*/ BSTR newVal)
{
m_bstrText = newVal;
return S_OK;
}
STDMETHOD(get_Text)(/*[out, retval]*/ BSTR *pVal)
{
*pVal = m_bstrText; // Oops! Call m_bstrText.Copy
// instead.
return S_OK;
}
};
由于m_bstrText 在get_Text结束没有超出作用域,你可能认为在the *pVal = m_bstrText 赋值时,不需要调用Copy。这是不对的。按照COM规则,调用者负责释放传出[out]参数的内容。由于*pVal指向了m_bstrText 封装的BSTR,而不是一个副本,调用者和m_bstrText 析构函数都会企图删除字符串。
·
循环中使用CComBSTR Objects
尽管CComBSTR可以分配buffer完成一些操作,例如: += operator或Append。但是,不推荐在一个小循环内部使用CComBSTR完成字符串操作。这种情况下,CString能提供更好的性能。
// This is not an efficient way
// to use a CComBSTR object.
CComBSTR bstrMyString;
while (bstrMyString.Length()<1000)
{
bstrMyString.Append(L"*");
}
1.2
_bstr_t Class
_bstr_t 是微软C++ COM扩展的一部分。_bstr_t封装了BSTR数据类型。_bstr_t通过SysAllocString and SysFreeString 等BSTR APIs管理资源的分配和释放。_bstr_t提供了内部引用计数来减少额外负担。
Construction | Version | | _bstr_t | | Constructs a _bstr_t object. | Operations | | | Assign | | Copies a BSTR into the BSTR wrapped by a _bstr_t. | Attach | VC 7 | Links a _bstr_t wrapper to a BSTR. | copy | | Constructs a copy of the encapsulated BSTR. | Detach | VC 7 | Returns the BSTR wrapped by a _bstr_t and detaches the BSTR from the _bstr_t. | GetAddress | VC 7 | Points to the BSTR wrapped by a _bstr_t. | GetBSTR | VC 7 | Points to the beginning of the BSTR wrapped by the _bstr_t. | length | | Returns the number of characters in the _bstr_t. | Operators | | | operator = | | Assigns a new value to an existing _bstr_t object. | operator += | | Appends characters to the end of the _bstr_t object. | operator + | | Concatenates two strings. | operator ! | | Checks if the encapsulated BSTR is a NULL string. | operator ==, !=, <, >, <=, >= | | Compares two _bstr_t objects. | operator wchar_t* | char* | | Extract the pointers to the encapsulated Unicode or multibyte BSTR object. | | | |
VC6中_bstr_t缺少了几个重要的方法:Attach/Detach/GetAddress/GetBSTR,所以比CComBSTR简单,使得_bstr_t的应用场合非常有限。而且,_bstr_t使用了引用计数在不同的对象间共享BSTR,内部实现比CComBSTR复杂。使用注意事项可以参考CComBSTR的类似函数。
建议只用于下面的情况:
·
BSTR的作用域管理
解决BSTR变量超出作用域范围的自动回收。(1)构造简单的BSTR对象,对BSTR进行基本字符串操作,作为输入[in]参数传递给被调用者。
{
_bstr_t bs1(L"first ");
bs1 += L"second ";
SetBs(bs1); // void SetBs(BSTR bs)
}
(2)作为BSTR的wrapper,解决[out]参数BSTR的生命周期之后的回收问题。
HRESULT BetterMethod()
{
BSTR val = NULL;
GetBs(&val); //void GetBs(/* [out] */ BSTR*)
_bstr_t bsVal(val, false);
// false is IMPORTANT. Other constructor could
// store the BSTR, too. But you must free the
// BSTR later.
}
HRESULT GoodMethod()
{
BSTR val = NULL;
GetBs(&val); //void GetBs(/* [out] */ BSTR*)
// All the function create a copy of BSTR.
// But you must free the BSTR immediately.
_bstr_t bsVal2(val);
_bstr_t bsVal3;
bsVal3 = val;
SysFreeString(val);
}
使用范围
完成简单的BSTR字符串连接、比较等操作。 |
|