Hello World!

程序员那点事儿

首页 新随笔 联系 聚合 管理
  20 Posts :: 6 Stories :: 0 Comments :: 0 Trackbacks
概述
  它被描述成一个与自动化相兼容的类型,由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码。因此BSTR实际上就是一个COM字符串,但它却在自动化技术以外的多种场合下得到广泛使用。

为什么需要BSTR

  COM是一种跨编程语言的平台,需要提供语言无关的数据类型。多数编程语言有自己的字符串表示。
  ●C++ 字符串是以0结束的ASCII或Unicode字符数组。
  ●Visual Basic字符串是一个ASCII字符数组加上表示长度的前缀。
  ●Java字符串是以0结束的Unicode字符数组。
  需要定义一种通用的字符串类型,可以很容易的匹配到不同编程语言。C++中,就是BSTR

什么是BSTR

  BSTR是“Basic STRing”的简称,微软在COM/OLE中定义的标准字符串数据类型。
  对于C++,Windows头文件wtypes.h中定义如下:
  typedef wchar_t WCHAR;
  typedef WCHAR OLECHAR;
  typedef OLECHAR __RPC_FAR *BSTR;;
  使用以Null结尾的简单字符串在COM component间传递不太方便。因此,标准BSTR是一个有长度前缀和null结束符的OLECHAR数组。BSTR的前4字节是一个表示字符串长度的前缀。BSTR长度域的值是字符串的字节数,并且不包括0结束符。
  由于是Unicode串,所以字符数是字节数的一半。这种方式的优点是允许程序员在BSTR串中间嵌入NULL字符。但是,BSTR的前四个字节表示长度,而OLECHAR数组的前四字节表示前两个字符。这种情况下,对于C++程序,如何实现BSTR和OLECHAR的交换?答案是COM提供了两个BSTR分配用的API:SysAllocString / SysReallocString。函数返回的指针指向BSTR的第一个字符,而不是BSTR在内存的第一个字节。

什么时候使用BSTR

  只有在你不得不用的时候。
  使用BSTR一般有以下几种情况:
  ●COM interface接口定义,并且不希望额外提供custom marshaling库(MDIL生成或开发人员自己订制),必须使用BSTR传递字符串。使用C/C++类型的字符串在COM DLL传递字符串,表面上可以使用,但违背了COM的基本规则,并且给以后的扩展留下了隐患。例如,把一个In-process COM Object(简单说COM DLL)改成out-of-process object(COM EXE)。理论上,客户端的代码应该不做任何改变。但如果是用了C/C++字符串,又希望只使用系统的automation mashaller(Oleaut32.dll),就会出错。
  ●如果可以提供custom marshaling,也推荐使用BSTR。
  ●客户要求接口必须使用BSTR,和客户讨论后,不能修改。
  ●使用的外部库的接口使用BSTR
  不使用的情况:
  ●不推荐在IDL结构体中定义BSTR成员,会给结构体的复制和释放带来麻烦。最好直接使用限定最大长度的TCHAR数组。如果确实需要传递变长字符串,BSTR应该被定义成独立的参数或者使用独立的get/set接口。
  ●尽可能缩小的BSTR及相关类型的作用域范围。类的成员变量和函数参数不使用BSTR。局部变量要尽快释放类的内部不使用BSTR。代码处理逻辑中只在接口直接相关部分使用BSTR。接收到一个BSTR时,尽量立刻变成C/C++的字符串副本进行处理。在需要传递BSTR参数前产生BSTR,用过立即释放。

BSTR、char*和CString转换 
  (1) char*转换成CString

  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:

char chArray[] = "This is a test"; 
char * p = "This is a test"; 

  或

LPSTR p = "This is a test"; 

  或在已定义Unicode应的用程序中

TCHAR * p = _T("This is a test"); 

  或

LPTSTR p = _T("This is a test"); 
CString theString = chArray; 
theString.Format(_T("%s"), chArray); 
theString = p; 

  (2) CString转换成char*

  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:

  方法一,使用强制转换。例如:

CString theString( "This is a test" ); 
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;  

  方法二,使用strcpy。例如:

CString theString( "This is a test" ); 
LPTSTR lpsz = new TCHAR[theString.GetLength()+1]; 
_tcscpy(lpsz, theString); 

  需要说明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。

  方法三,使用CString::GetBuffer。例如:

CString s(_T("This is a test ")); 
LPTSTR p = s.GetBuffer(); 
// 在这里添加使用p的代码 
if(p != NULL) *p = _T('\0'); 
s.ReleaseBuffer(); 
// 使用完后及时释放,以便能使用其它的CString成员函数 

  (3) BSTR转换成char*

  方法一,使用ConvertBSTRToString。例如:

#include 
#pragma comment(lib, "comsupp.lib") 
int _tmain(int argc, _TCHAR* argv[]){ 
BSTR bstrText = ::SysAllocString(L"Test"); 
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText); 
SysFreeString(bstrText); // 用完释放 
delete[] lpszText2; 
return 0; 
}  

  方法二,使用_bstr_t的赋值运算符重载。例如:

_bstr_t b = bstrText; 
char* lpszText2 = b; 

  (4) char*转换成BSTR

  方法一,使用SysAllocString等API函数。例如:

BSTR bstrText = ::SysAllocString(L"Test"); 
BSTR bstrText = ::SysAllocStringLen(L"Test",4); 
BSTR bstrText = ::SysAllocStringByteLen("Test",4); 

  方法二,使用COleVariant或_variant_t。例如:

//COleVariant strVar("This is a test"); 
_variant_t strVar("This is a test"); 
BSTR bstrText = strVar.bstrVal; 

  方法三,使用_bstr_t,这是一种最简单的方法。例如:

BSTR bstrText = _bstr_t("This is a test"); 

  方法四,使用CComBSTR。例如:

BSTR bstrText = CComBSTR("This is a test"); 

  或

CComBSTR bstr("This is a test"); 
BSTR bstrText = bstr.m_str; 

  方法五,使用ConvertStringToBSTR。例如:

char* lpszText = "Test"; 
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText); 

  (5) CString转换成BSTR

  通常是通过使用CStringT::AllocSysString来实现。例如:

CString str("This is a test"); 
BSTR bstrText = str.AllocSysString(); 
… 
SysFreeString(bstrText); // 用完释放  

  (6) BSTR转换成CString

  一般可按下列方法进行:

BSTR bstrText = ::SysAllocString(L"Test"); 
CStringA str; 
str.Empty(); 
str = bstrText;  

  或

CStringA str(bstrText); 

  (7) ANSI、Unicode和宽字符之间的转换

  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。

  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:

TCHAR tstr[] = _T("this is a test"); 
wchar_t wszStr[] = L"This is a test"; 
String* str = S”This is a test”; 

  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:

  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:

LPTSTR tstr= CA2TEX<16>("this is a test"); 
LPCTSTR tcstr= CA2CT("this is a test"); 
wchar_t wszStr[] = L"This is a test"; 
char* chstr = CW2A(wszStr);  

  六、结语

  几乎所有的程序都要用到字符串,而Visual C++.NET由于功能强大、应用广泛,因而字符串之间的转换更为频繁。本文几乎涉及到目前的所有转换方法。当然对于.NET框架来说,还可使用Convert和Text类进行不同数据类型以及字符编码之间的相互转换。
 
posted on 2012-03-28 10:52 hello wold! 阅读(19758) 评论(0)  编辑 收藏 引用 所属分类: 技术相关

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理