一、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并非第一选择,只有在你不得不用的时候在考虑吧!
使用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,用过立即释放。
二、VARIANT
VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体
tagVARIANT的定义。
struct tagVARIANT{union{struct __tagVARIANT{VARTYPE vt ;WORD wReserved1 ;WORD wReserved2 ;WORD wReserved3 ;union{LONG lVal ;BYTE bVal ;SHORT iVal ;FLOAT fltVal ;DOUBLE dblVal ;VARIANT_BOOL boolVal ;_VARIANT_BOOL bool ;SCODE scode ;CY cyVal ;DATE date ;BSTR bstrVal ;IUnknown __RPC_FAR * punkVal ;IDispatch __RPC_FAR * pdispVal ;SAFEARRAY __RPC_FAR * parray ;BYTE __RPC_FAR * pbVal ;SHORT __RPC_FAR * piVal ;LONG __RPC_FAR * plVal ;FLOAT __RPC_FAR * pfltVal ;DOUBLE __RPC_FAR * pdblVal ;VARIANT_BOOL __RPC_FAR * pboolVal ;_VARIANT_BOOL __RPC_FAR * pbool ;SCODE __RPC_FAR * pscode ;CY __RPC_FAR * pcyVal ;DATE __RPC_FAR * pdate ;BSTR __RPC_FAR * pbstrVal ;IUnknown __RPC_FAR * __RPC_FAR * ppunkVal ;IDispatch __RPC_FAR * __RPC_FAR * ppdispVal ;SAFEARRAY __RPC_FAR * __RPC_FAR * pparray ;VARIANT __RPC_FAR * pvarVal ;PVOID byref ;CHAR cVal ;USHORT uiVal ;ULONG ulVal ;INT intVal ;UINT uintVal ;DECIMAL __RPC_FAR * pdecVal ;CHAR __RPC_FAR * pcVal ;USHORT __RPC_FAR * puiVal ;ULONG __RPC_FAR * pulVal ;INT __RPC_FAR * pintVal ;UINT __RPC_FAR * puintVal ;struct __tagBRECORD{PVOID pvRecord ;IRecordInfo __RPC_FAR * pRecInfo ;} __VARIANT_NAME_4 ;} __VARIANT_NAME_3 ;} __VARIANT_NAME_2 ;DECIMAL decVal ;} __VARIANT_NAME_1 ;};
对于VARIANT变量的赋值:首先给vt成员赋值,指明数据类型,再对联合结构中相同数据类型的变量赋值,举个例子:
VARIANT va ;int a = 2001 ;va . vt = VT_I4 ; ///指明整型数据va.lVal=a; ///赋值
对于不马上赋值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为
VT_EMPTY,下表我们列举vt与常用数据的对应关系:
unsigned char bVal ; VT_UI1short iVal ; VT_I2long lVal ; VT_I4float fltVal ; VT_R4double dblVal ; VT_R8VARIANT_BOOL boolVal ; VT_BOOLSCODE scode ; VT_ERRORCY cyVal ; VT_CYDATE date ; VT_DATEBSTR bstrVal ; VT_BSTRIUnknown FAR * punkVal ; VT_UNKNOWNIDispatch FAR * pdispVal ; VT_DISPATCHSAFEARRAY FAR * parray ; VT_ARRAY |*unsigned char FAR * pbVal ; VT_BYREF | VT_UI1short FAR * piVal ; VT_BYREF | VT_I2long FAR * plVal ; VT_BYREF | VT_I4float FAR * pfltVal ; VT_BYREF | VT_R4double FAR * pdblVal ; VT_BYREF | VT_R8VARIANT_BOOL FAR * pboolVal ; VT_BYREF | VT_BOOLSCODE FAR * pscode ; VT_BYREF | VT_ERRORCY FAR * pcyVal ; VT_BYREF | VT_CYDATE FAR * pdate ; VT_BYREF | VT_DATEBSTR FAR * pbstrVal ; VT_BYREF | VT_BSTRIUnknown FAR * FAR * ppunkVal ; VT_BYREF | VT_UNKNOWNIDispatch FAR * FAR * ppdispVal ; VT_BYREF | VT_DISPATCHSAFEARRAY FAR * FAR * pparray ; VT_ARRAY |*VARIANT FAR * pvarVal ; VT_BYREF | VT_VARIANTvoid FAR * byref ; VT_BYREF
_variant_t是
VARIANT的封装类,其赋值可以使用强制类型转换,其构造函数会自动处理这些数据类型。
例如:
long l = 222 ;ing i = 100 ;_variant_t lVal ( l );lVal = ( long ) i ;
COleVariant的使用与
_variant_t的方法基本一样,请参考如下例子:
COleVariant v3 = "字符串", v4 = ( long ) 1999 ;CString str =( BSTR ) v3 . pbstrVal ;long i = v4 . lVal ;