Com中BSTR的使用

来源:互联网 发布:小米擦除数据 编辑:程序博客网 时间:2024/04/17 06:56
Com中BSTR的使用
详解一:

BSTR:
BSTR(Basic STRing)实际上是类Pascal字符串,在BSTR的前4个字节保存了字符串长度,所以BSTR内部可以包括/0,它也不是靠/0来判断字符串结尾的。BSTR在c/c++中的定义实际上是wchar_t*,但因为BSTR是依靠前4个字节来判断字符串长度的,所以它的分配和释放都不同于普通的字符串,需要通过::SysAllocString和::SysFreeString等函数来分配和释放。

Com的接口中要使用字符串的话必须使用BSTR类型,传个参数可以直接使用BSTR,传出参数则要使用BSTR*,也就是一个wchar_t的双重指针,它的作法其它是传递一个双重指针进来,由Com内部分配空间,并通过这个二级指针传递给客户程序,所以客户程序要负责释放这段空间(如果用户传进来的指针已经包含分配过的字符串,则在Com内部首先要释放原有的空间,然后再新分配一个返回,否则会有内存泄露),比如,假如接口里传入一个参数 BSTR* sAnswers,则需要首先:

if (*sAnswers != NULL)
{
    ::SysFreeString(*sAnswers);
    *sAnswers = NULL;
}
这个功能执行完要返回时再使用*sAnswers = ::SysAllocString(L"return value")分配一个新的字符串返回给客户。

客户调用时,如果需要通过这个BSTR*传值给Com,也必须使用::SysAllocString申请一段空间,然后把指针传进去,而不能使用局部变量(因为Com内部一般会首先释放这段空间,如果这个局部变量被Com释放,那它离开作用域的时候再自动析构可能就会出错),如果不需要传值进去(只作为接受返回值使用),则可以定义一个空的BSTR值(一般初始化为NULL,因为它也是一个指针)传入。不论是否传入值,经过Com接口传出的BSTR 都必须由客户来释放(使用::SysFreeString)。

相关函数:
::SysAllocString 根据参数分配一定长度的BSTR,并用传入参数初始化。

::SysFreeString 释放参数指向的BSTR

::SysAllocStringByteLen(LPCSTR psz, unsigned int len); 分配len字节的空间,拷贝psz的前len个字节用来初始化这个BSTR。

::SysAllocStringLen(const OLECHAR* pch, unsigned int cch); 分配一个新的BSTR,拷贝cch个字符进去。

::SysReAllocString(BSTR FAR* pbstr, const OLECHAR FAR* psz); 将pbstr指向的空间重新分配为psz的大小,并拷贝psz进去。如果pbstr为NULL,可能造成程序crash(因为内部assert)。

::SysReAllocStringLen(BSTR* pbstr, OLECHAR* psz, unsigned int cch); 释放旧的BSTR,然后按cch分配一个新的BSTR,并拷贝psz的前cch个字符进去。

::SysStringByteLen 返回BSTR的字节长度。

::SysStringLen 返回BSTR的字符长度。

OLECHAR和BSTR的区别:
BSTR不以/0结尾而领先前4个字节保存字符串的长度,所以需要用SysAllocString和SysFreeString来申请和释放内存。

 

=======================

 

 

详解二、

1        How to use BSTR
1.1      BSTR分析
BSTR设计对于C++程序员好坏参半。一方面,BSTR可以被用于大多数需要OLECHAR数组作为参数的函数。另一方面,不能用熟悉的C/C++函数进行对BSTR的分配、释放和处理,例如malloc, free, new, delete, lstrcat, and lstrlen 等函数不能用于处理BSTR。就像对接口指针和类指针的处理不一样,对BSTR的处理和对TCHAR*的处理也不一样。BSTR是一种C语言方式的类型定义方式,这种定义方式提高了BSTR在C++的应用效率,但是也带来了很多的潜在风险,它使程序员失去了利用编译器检查潜在问题的机会。
1.2      BSTR使用基本规则
 
在对BSTR进行读取操作的时候,可以把BSTR看作OLECHAR数组。BSTR可以用于const wchar_t*(LPCTSTR/ LPCWSTR/ cosnt TCHAR*/ cosnt WCHAR* in Unicode project),不能用于需要wchar_t* (LPTSTR/ LPWSTR/ TCHAR*/ WCHAR* in Unicode project)的地方。
如果有相应的BSTR处理函数,必须使用BSTR处理函数,不要使用普通字符串函数。特别是一个BSTR包含多个字符串(也就是,包含多个0结束符)的情况。在对BSTR进行修改(包括创建和释放时),必须使用BSTR的专用函数。主要要保证对字符长度前缀的正确修改。不要直接读取BSTR的长度域,应该使用BSTR处理函数计算长度。
 
String Manipulation Functions       Descriptions 
SysAllocString Creates and initializes a string.
SysAllocStringByteLen Creates a zero-terminated string of a specified length.
SysAllocStringLen Creates a string of a specified length.
SysFreeString Frees a previously created string.
SysReAllocString Changes the size and value of a string.
SysReAllocStringLen Changes the size of an existing string.
SysStringByteLen Returns the length of a string in bytes.
SysStringLen Returns the length of a string.

 
NULL是BSTR的有效值。按照约定,它可以被看作含有0个字符的字符串。BSTR变量必须等于NULL,或者正确分配的BSTR指针。在改变BSTR变量的之前,必须释放原来指向的BSTR。不要把BSTR直接初始化成常量字符指针,例如,BSTR bs = L””。
Automation会cache BSTR使用的空间,以提高SysAllocString/SysFreeString 的性能,会给测试发现问题带来困难。如果可能推荐在调试时使用Compuware DevPartner 7.x及更高版本的工具。
 
1.3      BSTR参数使用
多数时候,BSTR是被用于函数参数。关于BSTR参数的使用规则是BSTR类型的基础。只有熟练掌握,才能分析warpper类或转换函数的正确性。
 
 基本原则:在给by-reference[in/out]参数赋一个新的值前,被调用者负责释放。其他情况,都是调用者负责释放。
 
调用者使用BSTR的规则如下:
·         释放被调用函数返回的BSTR,或者被调用函数通过by-reference返回的BSTR。
HRESULT IWebBrowser2::get_StatusText( BSTR FAR* pbstr );
//...
BSTR bstrStatus;
pBrowser->get_StatusText( &bstrStatus );
 
// shows using the Win32 function
// to freee the memory for the string:
::SysFreeString( bstrStatus );
 
·         释放通过by-value方式传给其他函数的BSTR.
//.h
HRESULT IWebBrowser2::put_StatusText( BSTR bstr );
 
//.cpp
// shows using the Win32 function
// to allocate memory for the string:
BSTR bstrStatus = ::SysAllocString( L"Some text" );
if (bstrStatus == NULL)
   return E_OUTOFMEMORY;
 
pBrowser->put_StatusText( bstrStatus );
// Free the string:
::SysFreeString( bstrStatus );
//...
 
被调用者按照如下规则处理BSTR:
·         如果一个BSTR参数是by-reference方式,在给参数赋新值之前,Free以前的值。如果没有给参数赋的新值,不要Free传入值。
void RefreshBSTR(BSTR& bs)
// bs is an [in/out] parameter. BSTR* is the same
{
// using the bs here
Dosomething(bs);
// if (bs is about to be updated)
ASSERT(bs != NULL);
::SysReallocString(bs, _T(“NEW STRING”));
// SysReallocString will call SysFreeString and
// SysAllocString in sequence
// If bs is only [out] parameter, SysAllocString
// should be called here.
}
 
·         不要Free通过by-value传入的BSTR。
void SetBSTR(BSTR bs)
// bs is an [in] parameter. BSTR* is the same
{
// using the bs here
Dosomething(bs);
::SysFreeString(bs); //ERROR
}
 
·         不要Free返回给调用者的 BSTR .
BSTR GetBSTR1()
{
BSTR bs = ::SysAllocString(_T(“test”));
::SysFreeString(bs); //ERROR
return bs;
}
 
void GetBSTR2(BSTR* pBs)
{
CComBSTR bs(_T(“test”));
*pBS = (BSTR) bs; //ERROR: pBS will be freed automatically
}
 
·         如果需要保存传入的BSTR,被调用着需要用SysAllocString()生成一个新的副本,并保存。输入的BSTR会被调用者释放。
void MyClass::SetBSTR(BSTR bs)
{
//BSTR m_bs;
m_bs = bs; //ERROR
m_bs = ::SysReAllocString(bs);
}
·         如果需要返回一个已经存储的BSTR,返回BSTR的一个拷贝。调用者释放返回的BSTR拷贝。
void MyClass::GetBSTR(BSTR* pbs)
{
//BSTR m_bs;
*pbs = m_bs; //ERROR
*pbs = ::SysAllocString(m_bs);
}

 

===================

 

三(重要)!!!!!!!:

分配和释放 BSTR 的内存

参见:字符串 | CString::AllocSysString | SysAllocString | SysFreeString

当创建 BSTR 并在 COM 对象之间传递它们时,必须小心地处理它们所使用的内存以避免内存泄漏。当 BSTR 停留在接口中时,在完成其使用后必须释放出它的内存。但是,如果 BSTR 传递出了接口,那么接收对象将负责它的内存管理。

一般情况下,分配和释放分配给 BSTR 的内存的规则如下:

当调用进来一个需要 BSTR 参数的函数时,必须在调用之前为 BSTR 分配内存,并且在完成操作之后将其释放。例如:
HRESULT IWebBrowser2::put_StatusText( BSTR bstr );

// shows using the Win32 function
// to allocate memory for the string:
BSTR bstrStatus = ::SysAllocString( L"Some text" );
if (bstrStatus == NULL)
   return E_OUTOFMEMORY;

pBrowser->put_StatusText( bstrStatus );
// Free the string:
::SysFreeString( bstrStatus );
//...
当调用进来一个返回 BSTR 的函数时,必须自己来释放字符串。例如:
HRESULT IWebBrowser2::get_StatusText( BSTR FAR* pbstr );
//...
BSTR bstrStatus;
pBrowser->get_StatusText( &bstrStatus );

// shows using the Win32 function
// to freee the memory for the string:
::SysFreeString( bstrStatus );
当实现返回 BSTR 的函数时,请分配字符串,但不要释放它。接收函数会释放内存。例如:
// Example shows using MFC's
// CString::AllocSysString

//...
HRESULT CMyClass::get_StatusText( BSTR * pbstr )
{

   try
   {
      //m_str is a CString in your class
      *pbstr = m_str.AllocSysString( );
      }
   catch (...)
   {
      return E_OUTOFMEMORY;
   }

// The client is now responsible for freeing pbstr.
return( S_OK );
}
//...
请参见
字符串 | CString::AllocSysString | SysAllocString | SysFreeString

原创粉丝点击