BSTR详解四 - BSTR包容类

    技术2022-05-11  83

    转自 http://blog.csdn.net/pkrobbie/ 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字符串连接、比较等操作。  

    最新回复(0)