首页产品库评测行情新闻|手机数码笔记本台式机DIY硬件数字家庭数码相机办公外设|软件下载游戏开发|社区

更多

数码相机
MP4
LCD
机箱
音箱

软件资讯设计 工具 系统 开发 安全 办公 陶吧 IT教育 Vista频道 | 下载中心酷我音乐盒 腾讯QQ
天极网 > 开发频道>COM 组件设计与应用之数据类型

COM 组件设计与应用之数据类型

2005-06-27 11:01作者:杨老师出处:vckbase责任编辑:方舟 


  四、BSTR

  COM 中除了使用一些简单标准的数据类型外(注2),字符串类型需要特别重点地说明一下。还记得原则吗?COM 组件是运行在分布式环境中的。通俗地说,你不能直接把一个内存指针直接作为参数传递给COM函数。你想想,系统需要把这块内存的内容传递到“地球另一 边”的计算机上,因此,我至少需要知道你这块内存的尺寸吧?不然让我如何传递呀?传递多少字节呀?!而字符串又是非常常用的一种类型,因此 COM 设计者引入了 BASIC 中字符串类型的表示方式---BSTR。BSTR 其实是一个指针类型,它的内存结构是:(输入程序片段 BSTR p = ::SysAllocString(L"Hello,你好");断点执行,然后观察p的内存)


图二、BSTR 内存结构

  BSTR 是一个指向 UNICODE 字符串的指针,且 BSTR 向前的4个字节中,使用DWORD保存着这个字符串的字节长度( 没有含字符串的结束符)。因此系统就能够正确处理并传送这个字符串到“地球另一 边”了。特别需要注意的是,由于BSTR的指针就是指向 UNICODE 串,因此 BSTR 和 LPOLESTR 可以在一定程度上混用,但一定要注意:

  有函数 fun(LPCOLESTR lp),则你调用 BSTR p=...; fun(p); 正确

  有函数 fun(const BSTR bstr),则你调用 LPCOLESTR p=...; fun(p); 错误!!!

  有关 BSTR 的处理函数:
 
API 函数 说明
SysAllocString() 申请一个 BSTR 指针,并初始化为一个字符串
SysFreeString() 释放 BSTR 内存
SysAllocStringLen() 申请一个指定字符长度的 BSTR 指针,并初始化为一个字符串
SysAllocStringByteLen() 申请一个指定字节长度的 BSTR 指针,并初始化为一个字符串
SysReAllocStringLen() 重新申请 BSTR 指针

CString 函数

说明

AllocSysString() 从 CString 得到 BSTR
SetSysString() 重新申请 BSTR 指针,并复制到 CString 中

CComBSTR 函数

ATL 的 BSTR 包装类。在 atlbase.h 中定义

Append()、AppendBSTR()、AppendBytes()、ArrayToBSTR()、BSTRToArray()、AssignBSTR()、Attach()、Detach()、Copy()、CopyTo()、Empty()、Length()、ByteLength()、ReadFromStream()、WriteToStream()、LoadString()、ToLower()、ToUpper()
运算符重载:!,!=,==,<,>,&,+=,+,=,BSTR
    太多了,但从函数名称不能看出其基本功能。详细资料,查看MSDN 吧。另外,左侧函数,有很多是 ATL 7.0 提供的,VC6.0 下所带的 ATL 3.0 不支持。
    由于我们将来主要用 ATL 开发组件程序,因此使用 ATL 的 CComBSTR 为主。VC也提供了其它的包装类 _bstr_t。

  五、各种字符串类型之间的转换

  1、函数 WideCharToMultiByte(),转换 UNICODE 到 MBCS。使用范例:

      LPCOLESTR lpw = L"Hello,你好";
      size_t wLen = wcslen( lpw ) + 1;  // 宽字符字符长度,+1表示包含字符串结束符
      
      int aLen=WideCharToMultiByte(  // 第一次调用,计算所需 MBCS 字符串字节长度
		CP_ACP,
		0,
		lpw,  // 宽字符串指针
		wLen, // 字符长度
		NULL,
		0,  // 参数0表示计算转换后的字符空间
		NULL,
		NULL);
	
      LPSTR lpa = new char [aLen];
	
      WideCharToMultiByte(
		CP_ACP,
		0,
		lpw,
		wLen,
		lpa,  // 转换后的字符串指针
		aLen, // 给出空间大小
		NULL,
		NULL);

      // 此时,lpa 中保存着转换后的 MBCS 字符串
      ... ... ... ...
      delete [] lpa;

    2、函数 MultiByteToWideChar(),转换 MBCS 到 UNICODE。使用范例:
      LPCSTR lpa = "Hello,你好";
      size_t aLen = strlen( lpa ) + 1;
      
      int wLen = MultiByteToWideChar(
		CP_ACP,
		0,
		lpa,
		aLen,
		NULL,
		0);
      
      LPOLESTR lpw = new WCHAR [wLen];
      MultiByteToWideChar(
		CP_ACP,
		0,
		lpa,
		aLen,
		lpw,
		wLen);
      ... ... ... ...
      delete [] lpw;

    3、使用 ATL 提供的转换宏。
 
A2BSTR OLE2A T2A W2A
A2COLE OLE2BSTR T2BSTR W2BSTR
A2CT OLE2CA T2CA W2CA
A2CW OLE2CT T2COLE W2COLE
A2OLE OLE2CW T2CW W2CT
A2T OLE2T T2OLE W2OLE
A2W OLE2W T2W W2T

  上表中的宏函数,其实非常容易记忆:

2 好搞笑的缩写,to 的发音和 2 一样,所以借用来表示“转换为、转换到”的含义。
A ANSI 字符串,也就是 MBCS。
W、OLE 宽字符串,也就是 UNICODE。
T 中间类型T。如果定义了 _UNICODE,则T表示W;如果定义了 _MBCS,则T表示A
C const 的缩写

  使用范例:

      #include <atlconv.h>
      
      void fun()
      {
          USES_CONVERSION;  // 只需要调用一次,就可以在函数中进行多次转换
          
          LPCTSTR lp = OLE2CT( L"Hello,你好") );
          ... ... ... ...
          // 不用显式释放 lp 的内存,因为
          // 由于 ATL 转换宏使用栈作为临时空间,函数结束后会自动释放栈空间。
      }
  使用 ATL 转换宏,由于不用释放临时空间,所以使用起来非常方便。但是考虑到栈空间的尺寸(VC 默认2M),使用时要注意几点:

    1、只适合于进行短字符串的转换;

    2、不要试图在一个次数比较多的循环体内进行转换;

    3、不要试图对字符型文件内容进行转换,因为文件尺寸一般情况下是比较大的;

    4、对情况 2 和 3,要使用 MultiByteToWideChar() 和 WideCharToMultiByte();
   
  六、VARIANT

  C++、BASIC、Java、Pascal、Script......计算机语言多种多样,而它们各自又都有自己的数据类型,COM 产生目的,其中之一就是要跨语言(注3)。而 VARIANT 数据类型就具有跨语言的特性,同时它可以表示(存储)任意类型的数据。从C语言的角度来讲,VARIANT 其实是一个结构,结构中用一个域(vt)表示------该变量到底表示的是什么类型数据,同时真正的数据则存贮在 union 空间中。结构的定义太长了(虽然长,但其实很简单)大家去看 MSDN 的描述吧,这里给出如何使用的简单示例:

  学生:我想用 VARIANT 表示一个4字节长的整数,如何做?

  老师:VARIANT v; v.vt=VT_I4; v.lVal=100;

  学生:我想用 VARIANT 表示布尔值“真”,如何做?
 
  老师:VARIANT v; v.vt=VT_BOOL; v.boolVal=VARIANT_TRUE;

  学生:这么麻烦?我能不能 v.boolVal=true; 这样写?

  老师:不可以!因为
 
类型 字节长度 假值 真值
bool 1(char) 0(false) 1(true)
BOOL 4(int) 0(FALSE) 1(TRUE)
VT_BOOL 2(short int) 0(VARIANT_FALSE) -1(VARIANT_TRUE)

  所以如果你 v.boolVal=true 这样赋值,那么将来 if(VARIANT_TRUE==v.boolVal) 的时候会出问题(-1 != 1)。但是你注意观察,任何布尔类型的“假”都是0,因此作为一个好习惯,在做布尔判断的时候,不要和“真值”相比较,而要与“假值”做比较。

  学生:谢谢老师,你太牛了。我对老师的敬仰如滔滔江水,连绵不绝......

  学生:我想用 VARIANT 保存字符串,如何做?
 
  老师:VARIANT v; v.vt=VT_BSTR; v.bstrVal=SysAllocString(L"Hello,你好");

  学生:哦......我明白了。可是这么操作真够麻烦的,有没有简单一些的方法?
 
  老师:有呀,你可以使用现成的包装类 CComVariant、COleVariant、_variant_t。比如上面三个问题就可以这样书写:CComVariant v1(100),v2(true),v3("Hello,你好"); 简单了吧?!(注4)

  学生:老师,我再问最后一个问题,我如何用 VARIANT 保存一个数组?

  老师:这个问题很复杂,我现在不能告诉你,我现在告诉你怕你印象不深......(注5)

  学生:~!@#$%^&*()......晕!

关注此文的读者还看过:

返回开发频道首页

共2页。 上一页12

软件频道最新更新

热点推荐

IT嘉年华

编辑推荐

软件下载

热门
推荐

网友关注

软件
资料
游戏

装机推荐

文章排行

本周
本月
最新更新
天极服务|关于我们|About us|网站律师|RSS订阅|友情合作|加入我们|天极动态|网站地图|意见反馈|MSN/QQ上看天极
Copyright (C) 1999-2012 Yesky.com, All Rights Reserved 版权所有 天极网络