BSTR

概述
  它被描述成一個與自動化相兼容的類型,由於操作系統提供相應的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類進行不同數據類型以及字符編碼之間的相互轉換。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章