理解BSTR数据类型 神奇的BSTR - 深蓝的日志 - 网易博客 http://blog.163.com/pugood@126/blog/static/1344175932009111111526409/

多数支持COM的语言都没法处理单以NULL结尾的字符数组(不管是否是UNICODE)。

Visual Basic, Java, VBScript, 还有 Jscript都希望字符串是固定字节长度的。

BSTR数据类型是一个UNICODE,固定字节长度,且以NULL结尾的字符串,所有的COM兼容语言都 可以使用。

虽然所有的COM兼容的语言都能使用BSTR,但它们都以自己的方式操作。VB程序员用下面的代码创建BSTR:

' VB developer made a BSTR.
'
Dim name as String
name = "Fred Flintstone"

作为C++程序员,我们使用一组COM包创建,操作BSTR数据。每个BSTR方法名都有"Sys-"前缀,用以表示是操作BSTR的(system string)。

比较有趣的是BSTR是个OLECHAR*的typedef,所以它是一个OLECHAR字符数组。

// Behold the BSTR (<wtypes.h>).
typedef OLECHAR*          BSTR;

操作BSTR的方法才是真正有区别的地方。下面我们就看一下常用的BSTR方法,了解什么时候去使用它。


在C++中创建BSTR

当你要在C++中创建BSTR的时候,你需要使用SysAllocString()。这个方法会计算字符串的长度并设置足够的缓存。例如,我们传入一个UNICODE的字符串,用bstrName变量来保持返回值:

// SysAllocString() creates a BSTR.
BSTR bstrName;
bstrName = SysAllocString(L"Fred Flintstone");

当然,多数情况下你不想使用硬代码的字符串来初始化BSTR,而使用变量。应此,你可以使用OLECHAR*变量来创建BSTR(使用OLESTR宏来确保使用正确的类型):

// Create a BSTR using an array of OLECHAR types (could be char or wchar_t).
OLECHAR* pOLEStr;     
pOLEStr = OLESTR("Fred Flinstone");
BSTR bstrName;
bstrName = SysAllocString(pOLEStr);

操作BSTR

一旦你创建了一个BSTR,你很有可能在程序中会重设它的值。使用SysReAllocString()来修改一个现有的BSTR,它会释放之前的空间,重新计算字符串的长度和设置缓存:

// Change existing bstrName to 'Mr. Slate'
SysReAllocString(&bstrName, L"Mr. Slate");

SysStringLen()方法给你计算现存BSTR缓存的长度:

// Mr. Slate == 9
int length = SysStringLen(bstrName);

重要的是,任何一个使用SysAllocString()创建BSTR必须使用SysFreeString()来清除。任何一个你从接口方法获得的BSTR也需要使用SysFreeString()来清除。

// All done with the string.
SysFreeString(bstrName);

注意:忘记使用SysFreeString()来清除BSTR会导致内存泄漏。这个的重要性就和在C++中用NEW来获得内存而忘记用DELETE删除是一样的。

额外的BSTR方法

SysAllocString(), SysStringLen(), 和 SysFreeString()方法是学习操作BSTR的好开始,BSTR的API还定义了一些其他的方法。这里列举 了<oleauto.h>中定义的所有的方法,在线帮助里有更完善的注解:

SysAllocString()
创建一个新的BSTR 。

SysReAllocString()
重新设置一个已存在的BSTR 。

SysStringLen()
返回BSTR 的长度。

SysFreeString()
销毁已存在的BSTR 。

SysReAllocStringLen()
Used to create a BSTR based on some length of characters.

SysStringByteLen()
返回BSTR的字节长度。(Win32)

SysAllocStringByteLen()
使用二进制数据来创建BSTR。你只能在不存在ANSI 到Unicode 或是Unicode 到ANSI转换的情况下使用。(Win32)

Unicode 到 ANSI转换

即使我们都能接纳BSTR(最大化的做到语言独立性),我们还有一个未解决的问题。WIN32 API的字符串参数通常都是ANSI类型。如:我们广泛使用的MessageBox()看起来是这样:

// This is the MessageBox() method we think we know...
MessageBox (HWND hWnd , LPCSTR lpText, LPCSTR lpCaption, UINT uType);

根据上面的方法原型,它看起来像是我们需要提供2个字符数组常量(LPCSTR =指向常量字符数组的长指针)。然而,现实总是很奇怪,事实上WIN32 API中根本就没有MessageBox()方法。实际上这个方法(所有包含字符串参数的WIN32方法)定义成2个可能的形式:

// Every Win32 function which takes text strings has an ANSI (A) or Unicode(W)
// variation.
#ifdef UNICODE
     #define MessageBox MessageBoxW
#else
     #define MessageBox MessageBoxA
#endif // !UNICODE

在WIN NT下,当你选择使用UNICODE编译你的当前项目时,就会定义UNICODE预处理标志(在Project | Settings菜单中选择)。在这种情况下,API中所有的方法就会自动转变成宽字符版本。例如MessageBox()会转换为下面的形式:

// Under Unicode builds, all strings come through as an array of constant wchar_t.
MessageBoxW( HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);

在非UNICODE结构下,MessageBox()转变成ANSI字符版本:

// ANSI builds use const char arrays.
MessageBoxA (HWND hWnd , LPCSTR lpText, LPCSTR lpCaption, UINT uType);

我们遇到了进退两难的情况,如果我们选择UNICODE结构,我们的项目就只能在WIN NT下正确运行。如果我们选择非UNICODE结构,程序就可以运行在所有的平台上,虽然在UNICODE平台上(比如WIN NT)会执行ANSI到UNICODE的转换(意味着会降低效率)。

用于转换的方法

WIN32定义了2个很强大的方法,让你将ANSI转换成UNICODE,或是将UNICODE转换成ANSI。这2个方法给你最大的灵活性。但是鉴于他们复杂的参数,稍显难用:

? MultiByteToWideChar(): Converts an ANSI string to a Unicode equivalent.
? WideCharToMultiByte(): Converts a Unicode string to an ANSI equivalent.

还有一个选择,C的运行期库提供了较为简单,方便,且跨平台的转换方法。如果你想将UNICODE(例如BSTR)字符串转换为ANSI字符串,可以调用wcstombs()方法(Wide Character String to Multibyte String):

// wcstombs( char *ANSIString, wchar_t *UNICODEString, size_t count );
char buff[MAX_LENGTH];
BSTR bstr = SysAllocString(L"How did strings get so ugly?");
wcstombs(buff, bstr, MAX_LENGTH);          // P3 = size of target buffer.
cout << buff << endl;                      // Pump to console.
SysFreeString(bstr);

如果你想将ANSI字符串转换为UNICODE,调用mbstowcs()方法(Multi Byte String to Wide Character String):

// Transform an existing char* (ANSI) into a wchar_t* (Unicode)
mbstowcs( wchar_t *UNICODEString , char *ANSIString, size_t count );

当我们从COM转移到ATL后,我们将有一整套用于转换的宏,简化字符的转换操作,忘掉之前的4个方法。同时,ATL中的CComBSTR类将我们从复杂的字符串方法中解救出来。但是现在,我们还有很长的一段路要走,也仍然要使用那些转换的方法。

理解BSTR数据类型 神奇的BSTR的更多相关文章

  1. 关于BSTR数据类型

    关于BSTR数据类型 - 极品垃圾 - C++博客 http://www.cppblog.com/bestcln/articles/82712.html VC++常用数据类型及其操作详解(非常经典,共 ...

  2. java基础总结(1)--深入理解基本数据类型

    深入理解java数据类型 java是一种强类型语言,这就意味着必须为每一个声明变量声明一种类型.在java中,一共有8种数据类型,其中4种整型,2种浮点类型,1种字符类型和一种表示真值的boolean ...

  3. Java基础系列1:深入理解Java数据类型

    Java基础系列1:深入理解Java数据类型 当初学习计算机的时候,教科书中对程序的定义是:程序=数据结构+算法,Java基础系列第一篇就聊聊Java中的数据类型. 本篇聊Java数据类型主要包括四个 ...

  4. 深入理解redis数据类型

    转载请注明出处:https://www.cnblogs.com/wenjunwei/p/9720033.html redis的存储模型 redis不是普通的键值对存储,它实际上是一个数据结构存储服务器 ...

  5. 理解JavaScript数据类型

    JavaScript有5种基本数据类型: 数值(number):整数和小数(比如1和3.14) 字符串(string):字符组成的文本(比如"Hello World") 布尔值(b ...

  6. 一篇文章理解JS数据类型、深拷贝和浅拷贝

    前言 笔者最近整理了一些前端技术文章,如果有兴趣可以参考这里:muwoo blogs.接下来我们进入正片: js 数据类型 六种 基本数据类型: Boolean. 布尔值,true 和 false. ...

  7. VC中BSTR、Char和CString类型的转换

    1.char*转换成CString 若将char*转换成CString,除了直接赋值外,还可使用CString::format进行.例如: char chArray[] = "This is ...

  8. ATL接口返回类型&&ATL接口返回字符串BSTR*

    感觉在ATL中做COM组件,添加方法的时候,其返回值只能是HRESULT,我想返回其他数据类型,可以吗? 非也非也 HRESULT指示返回的状态,即正确与否, 返回值是这样的 HRESULT MyMe ...

  9. CString char BSTR 转换

     关于字符集不一的历史原因,可以参考: UNICODE与ANSI的区别 以下是网上转载的资料.我将辅以自己的实例,说明并总结关系. 一.CString, int, string, char*之间的转换 ...

随机推荐

  1. Mongodb查询引用

    var result = db.users.findOne({"name":"Tom Wu"},{"address_ids":1,_id:0 ...

  2. angular学习笔记(二十七)-$http(5)-使用$http构建RESTful架构

    在angular中有一个特别为RESTful架构而定制的服务,是在$http的基础上进行了封装. 但是为了学习,我们先看看用直接$http是如何构建RESTful架构的: 假设有一个银行卡的列表.需要 ...

  3. 字符编码:ASCII,Unicode,UTF-8

    1.ASCII码美国制定的一套字符编码,对英语字符和二进制位之间的关系,做了统一规定.ASCII码一共规定了128个字符(包括32个不能打印出来的控制符号)的编码,占用一个字节,字节的最前面1位统一为 ...

  4. 简单的图形学(二)——材质与反射

    在上一篇[游戏框架系列]简单的图形学(一)文章中,我们讲述了光线追踪的一个最简单的操作--依每个像素延伸出一条追踪光线,光线打到球上(产生交点),就算出这条线的长度,作为最终的灰度,打不到球上,就显示 ...

  5. vue自动化单元测试

    // 引用vue和需要测试的组件 import Vue from 'vue' import HelloWorld from '@/components/HelloWorld' // 创建测试套件,一个 ...

  6. 重装Ubuntu系统

    1.安装JDK参考:http://weixiaolu.iteye.com/blog/1401786jdk-6u31-linux-i586.bin莫名奇妙的安装失败.所以下载了jdk-7u45-linu ...

  7. mybatis深入学习

    最近做的一个活可以让我深入学习一下现在比较流行的ORM框架:mybatis/ibatis的内部原理,SQL的拦截,解析,dataSource和JDBC中做一些额外的事情.如果有可能的话想造一个比较简单 ...

  8. C语言 · 前10名

    算法提高 前10名   时间限制:1.0s   内存限制:256.0MB      问题描述 数据很多,但我们经常只取前几名,比如奥运只取前3名.现在我们有n个数据,请按从大到小的顺序,输出前10个名 ...

  9. SSL/TLS原理详解2

    引用原文地址:https://segmentfault.com/a/1190000004985253#articleHeader6 在进行 HTTP 通信时,信息可能会监听.服务器或客户端身份伪装等安 ...

  10. nginx日志分析利器GoAccess(转)

    面试的时候一定会被面到的问题是:给出web服务器的访问日志,请写一个脚本来统计访问前10的IP有哪些?访问前10的请求有哪些?当你领略过goaccess之后,你就明白,这些问题,除了考验你的脚本背诵记 ...