




已阅读5页,还剩19页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C+字符串完全指南 Win32字符编码关键词: 字符串 c+ 前言字符串的表现形式各异,象TCHAR,std:string,BSTR等等,有时还会见到怪怪的用_tcs起头的宏。这个指南的目的就是说明各种字符串类型及其用途,并说明如何在必要时进行类型的相互转换。在指南的第一部分,介绍三种字符编码格式。理解编码的工作原理是致为重要的。即使你已经知道字符串是一个字符的数组这样的概念,也请阅读本文,它会让你明白各种字符串类之间的关系。指南的第二部分,将阐述各个字符串类,什么时候使用哪种字符串类,及其相互转换。字符串基础 ASCII, DBCS, Unicode所有的字符串类都起源于C语言的字符串,而C语言字符串则是字符的数组。首先了解一下字符类型。有三种编码方式和三种字符类型。第一种编码方式是单字节字符集,称之为SBCS,它的所有字符都只有一个字节的长度。ASCII码就是SBCS。SBCS字符串由一个零字节结尾。第二种编码方式是多字节字符集,称之为MBCS,它包含的字符中有单字节长的字符,也有多字节长的字符。Windows用到的MBCS只有二种字符类型,单字节字符和双字节字符。因此Windows中用得最多的字符是双字节字符集,即DBCS,通常用它来代替MBCS。在DBCS编码中,用一些保留值来指明该字符属于双字节字符。例如,Shift-JIS(通用日语)编码中,值081-09F 和 0xE0-0xFC 的意思是:“这是一个双字节字符,下一个字节是这个字符的一部分”。这样的值通常称为前导字节(lead byte),总是大于07F。前导字节后面是跟随字节(trail byte)。DBCS的跟随字节可以是任何非零值。与SBCS一样,DBCS字符串也由一个零字节结尾。第三种编码方式是Unicode。Unicode编码标准中的所有字符都是双字节长。有时也将Unicode称为宽字符集(wide characters),因为它的字符比单字节字符更宽(使用更多内存)。注意,Unicode不是MBCS 区别在于MBCS编码中的字符长度是不同的。Unicode字符串用二个零字节字符结尾(一个宽字符的零值编码)。单字节字符集是拉丁字母,重音文字,用ASCII标准定义,用于DOS操作系统。双字节字符集用于东亚和中东语言。Unicode用于COM和Windows NT内部。读者都很熟悉单字节字符集,它的数据类型是char。双字节字符集也使用char数据类型(双字节字符集中的许多古怪处之一)。Unicode字符集用wchar_t数据类型。Unicode字符串用L前缀起头,如: wchar_t wch = L1; / 2 个字节, 00031 wchar_t* wsz = LHello; / 12 个字节, 6 个宽字符字符串的存储单字节字符串顺序存放各个字符,并用零字节表示字符串结尾。例如,字符串Bob的存储格式为:Unicode编码中,LBob的存储格式为:用00000 (Unicode的零编码)结束字符串。DBCS 看上去有点象SBCS。以后我们会看到在串处理和指针使用上是有微妙差别的。字符串日本语 (nihongo) 的存储格式如下(用LB和TB分别表示前导字节和跟随字节):注意,ni的值不是WORD值0xFA93。值93和FA顺序组合编码为字符ni。(在高位优先CPU中,存放顺序正如上所述)。字符串处理函数C语言字符串处理函数,如strcpy(), sprintf(), atol()等只能用于单字节字符串。在标准库中有只用于Unicode字符串的函数,如wcscpy(), swprintf(), _wtol()。微软在C运行库(CRT)中加入了对DBCS字符串的支持。对应于strxxx()函数,DBCS使用_mbsxxx()函数。在处理DBCS字符串(如日语,中文,或其它DBCS)时,就要用_mbsxxx()函数。这些函数也能用于处理SBCS字符串(因为DBCS字符串可能就只含有单字节字符)。现在用一个示例来说明字符串处理函数的不同。如有Unicode字符串LBob:x86 CPU的排列顺序是低位优先(little-endian)的,值00042的存储顺序为42 00。这时如用strlen()函数求字符串的长度就发生问题。函数找到第一个字节42,然后是00,意味着字符串结尾,于是返回1。反之,用wcslen()函数求Bob的长度更糟糕。wcslen()首先找到06F42,然后是00062,以后就在内存缓冲内不断地寻找00 00直至发生一般性保护错(GPF)。strxxx()及其对应的_mbsxxx()究竟是如何运作的?二者之间的不同是非常重要的,直接影响到正确遍历DBCS字符串的方法。下面先介绍字符串遍历,然后再回来讨论strxxx()和 _mbsxxx()。字符串遍历我们中的大多数人都是从SBCS成长过来的,都习惯于用指针的 + 和 操作符来遍历字符串,有时也使用数组来处理字符串中的字符。这二种方法对于SBCS 和 Unicode 字符串的操作都是正确无误的,因为二者的字符都是等长的,编译器能够的正确返回我们寻求的字符位置。但对于DBCS字符串就不能这样了。用指针访问DBCS字符串有二个原则,打破这二个原则就会造成错误。1. 不可使用 + 算子,除非每次都检查是否为前导字节。2. 绝不可使用 算子来向后遍历。先说明原则2,因为很容易找到一个非人为的示例。假设,有一个配制文件,程序启动时要从安装路径读取该文件,如:C:Program FilesMyCoolAppconfig.bin。文件本身是正常的。假设用以下代码来配制文件名:bool GetConfigFileName ( char* pszName, size_t nBuffSize )char szConfigFilenameMAX_PATH; / 这里从注册表读取文件的安装路径,假设一切正常。 / 如果路径末尾没有反斜线,就加上反斜线。 / 首先,用指针指向结尾零:char* pLastChar = strchr ( szConfigFilename, 0 ); / 然后向后退一个字符: pLastChar-; if ( *pLastChar != ) strcat ( szConfigFilename, ); / 加上文件名: strcat ( szConfigFilename, config.bin ); / 如果字符串长度足够,返回文件名: if ( strlen ( szConfigFilename ) = nBuffSize ) return false; else strcpy ( pszName, szConfigFilename ); return true; 这段代码的保护性是很强的,但用到DBCS字符串还是会出错。假如文件的安装路径用日语表达:C:,该字符串的内存表达为:这时用上面的GetConfigFileName()函数来检查文件路径末尾是否含有反斜线就会出错,得到错误的文件名。错在哪里?注意上面的二个十六进制值05C(蓝色)。前面的05C是字符,后面则是字符值83 5C,代表字符。可是函数把它误认为反斜线了。正确的方法是用DBCS函数将指针指向恰当的字符位置,如下所示:bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )char szConfigFilenameMAX_PATH; / 这里从注册表读取文件的安装路径,假设一切正常。 / 如果路径末尾没有反斜线,就加上反斜线。 / 首先,用指针指向结尾零:char* pLastChar = _mbschr ( szConfigFilename, 0 ); / 然后向后退一个双字节字符: pLastChar = CharPrev ( szConfigFilename, pLastChar ); if ( *pLastChar != ) _mbscat ( szConfigFilename, ); / 加上文件名: _mbscat ( szConfigFilename, config.bin ); / 如果字符串长度足够,返回文件名: if ( _mbslen ( szInstallDir ) = nBuffSize ) return false; else _mbscpy ( pszName, szConfigFilename ); return true; 这个改进的函数用CharPrev() API 函数将指针pLastChar向后移动一个字符。如果字符串末尾的字符是双字节字符,就向后移动2个字节。这时返回的结果是正确的,因为不会将字符误判为反斜线。现在可以想像到第一原则了。例如,要遍历字符串寻找字符:,如果不使用CharNext()函数而使用+算子,当跟随字节值恰好也是:时就会出错。与原则2相关的是数组下标的使用:2a. 绝不可在字符串数组中使用递减下标。出错原因与原则2相同。例如,设置指针pLastChar为:char* pLastChar = &szConfigFilename strlen(szConfigFilename) - 1;结果与原则2的出错一样。下标减1就是指针向后移动一个字节,不符原则2。再谈strxxx() 与_mbsxxx()现在可以清楚为什么要用 _mbsxxx() 函数了。strxxx() 函数不认识DBCS字符而 _mbsxxx()认识。如果调用strrchr(C:, )函数可能会出错,但 _mbsrchr()认识双字节字符,所以能返回指向最后出现反斜线字符的指针位置。最后提一下strxxx() 和 _mbsxxx() 函数族中的字符串长度测量函数,它们都返回字符串的字节数。如果字符串含有3个双字节字符,_mbslen()将返回6。而Unicode的函数返回的是wchar_ts的数量,如wcslen(LBob) 返回3C+字符串完全指南 Win32字符编码(二)翻译:连波 15/11/2002 URL: /developer/tech/story/0,2000081602,39098306,00.htm Win32 API中的MBCS 和 UnicodeAPI的二个字符集也许你没有注意到,Win32的API和消息中的字符串处理函数有二种,一种为MCBS字符串,另一种为Unicode字符串。例如,Win32中没有SetWindowText()这样的接口,而是用SetWindowTextA()和 SetWindowTextW()函数。后缀A (表示ANSI)指明是MBCS函数,后缀W(表示宽字符)指明是Unicode函数。编写Windows程序时,可以选择用MBCS或Unicode API接口函数。用VC AppWizards向导时,如果不修改预处理器设置,缺省使用的是MBCS函数。但是在API接口中没有SetWindowText()函数,该如何调用呢?实际上,在winuser.h头文件中做了以下定义:BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );#ifdef UNICODE#define SetWindowText SetWindowTextW#else#define SetWindowText SetWindowTextA#endif编写MBCS应用时,不必定义UNICODE,预处理为:#define SetWindowText SetWindowTextA然后将SetWindowText()处理为真正的API接口函数SetWindowTextA() (如果愿意的话,可以直接调用SetWindowTextA() 或SetWindowTextW()函数,不过很少有此需要)。如果要将缺省应用接口改为Unicode,就到预处理设置的预处理标记中去掉 _MBCS标记,加入UNICODE 和 _UNICODE (二个标记都要加入,不同的头文件使用不同的标记)。不过,这时要处理普通字符串反而会遇到问题。如有代码:HWND hwnd = GetSomeWindowHandle();char szNewText = we love Bob!;SetWindowText ( hwnd, szNewText );编译器将SetWindowText置换为SetWindowTextW后,代码变为:HWND hwnd = GetSomeWindowHandle();char szNewText = we love Bob!;SetWindowTextW ( hwnd, szNewText );看出问题了吧,这里用一个Unicode字符串处理函数来处理单字节字符串。第一种解决办法是使用宏定义:HWND hwnd = GetSomeWindowHandle();#ifdef UNICODEwchar_t szNewText = Lwe love Bob!;#elsechar szNewText = we love Bob!;#endifSetWindowText ( hwnd, szNewText );要对每一个字符串都做这样的宏定义显然是令人头痛的。所以用TCHAR来解决这个问题:TCHAR的救火角色TCHAR 是一种字符类型,适用于MBCS 和 Unicode二种编码。程序中也不必到处使用宏定义。TCHAR的宏定义如下:#ifdef UNICODEtypedef wchar_t TCHAR;#elsetypedef char TCHAR;#endif所以,TCHAR中在MBCS程序中是char类型,在Unicode中是 wchar_t 类型。对于Unicode字符串,还有个 _T() 宏,用于解决 L 前缀:#ifdef UNICODE#define _T(x) L#x#else#define _T(x) x#endif# 是预处理算子,将二个变量粘贴在一起。不管什么时候都对字符串用 _T 宏处理,这样就可以在Unicode编码中给字符串加上L前缀,如:TCHAR szNewText = _T(we love Bob!);SetWindowTextA/W 函数族中还有其它隐藏的宏可以用来代替strxxx() 和 _mbsxxx() 字符串函数。例如,可以用 _tcsrchr 宏取代strrchr(),_mbsrchr(),或 wcsrchr()函数。_tcsrchr 根据编码标记为_MBCS 或 UNICODE,将右式函数做相应的扩展处理。宏定义方法类似于SetWindowText。不止strxxx()函数族中有TCHAR宏定义,其它一些函数中也有。例如,_stprintf (取代sprintf()和swprintf(),和 _tfopen (取代fopen() 和 _wfopen()。MSDN的全部宏定义在Generic-Text Routine Mappings栏目下。String 和 TCHAR 类型定义Win32 API 文件中列出的函数名都是通用名(如SetWindowText),所有的字符串都按照TCHAR类型处理。(只有XP除外,XP只使用Unicode类型)。下面是MSDN给出的常用类型定义:类型MBCS 编码中的意义Unicode 编码中的意义WCHARwchar_twchar_tLPSTRzero-terminated string of char (char*) zero-terminated string of char (char*) LPCSTRconstant zero-terminated string of char (constchar*) constant zero-terminated string of char (constchar*) LPWSTRzero-terminated Unicode string (wchar_t*) zero-terminated Unicode string (wchar_t*) LPCWSTRconstant zero-terminated Unicode string (const wchar_t*) constant zero-terminated Unicode string (const wchar_t*) TCHARcharwchar_tLPTSTRzero-terminated string of TCHAR (TCHAR*) zero-terminated string of TCHAR (TCHAR*) LPCTSTRconstant zero-terminated string of TCHAR (const TCHAR*) constant zero-terminated string of TCHAR (const TCHAR*) 何时使用TCHAR 和Unicode可能会有疑问:“为什么要用Unicode?我一直用的都是普通字符串。”在三种情况下要用到Unicode:1. 程序只运行于Windows NT。2. 处理的字符串长于MAX_PATH定义的字符数。 3. 程序用于Windows XP中的新接口,那里没有A/W版本之分。 大部分Unicode API不可用于Windows 9x。所以如果程序要在Windows 9x上运行的话,要强制使用MBCS API (微软推出一个可运行于Windows 9x的新库,叫做Microsoft Layer for Unicode。但我没有试用过,无法说明它的好坏)。相反,NT内部全部使用Unicode编码,使用Unicode API可以加速程序运行。每当将字符串处理为MBCS API时,操作系统都会将字符串转换为Unicode并调用相应的Unicode API 函数。对于返回的字符串,操作系统要做同样的转换。尽管这些转换经过了高度优化,模块尽可能地压缩到最小,但毕竟会影响到程序的运行速度。NT允许使用超长文件名(长于MAX_PATH 定义的260),但只限于Unicode API使用。Unicode API的另外一个优点是程序能够自动处理输入的文字语言。用户可以混合输入英文,中文和日文作为文件名。不必使用其它代码来处理,都按照Unicode编码方式处理。最后,作为Windows 9x的结局,微软似乎抛弃了MBCS API。例如,SetWindowTheme() 接口函数的二个参数只支持Unicode编码。使用Unicode编码省却了MBCS与Unicode之间的转换过程。如果程序中还没有使用到Unicode编码,要坚持使用TCHAR和相应的宏。这样不但可以长期保持程序中DBCS编码的安全性,也利于将来扩展使用到Unicode编码。那时只要改变预处理中的设置即可!C+字符串完全指南(2) 各种字符串类(一)翻译:连波 19/11/2002 URL: /developer/tech/story/0,2000081602,39098621,00.htm 前言C语言的字符串容易出错,难以管理,并且往往是黑客到处寻找的目标。于是,出现了许多字符串包装类。可惜,人们并不很清楚什么情况下该用哪个类,也不清楚如何将C语言字符串转换到包装类。本文涉及到Win32 API,MFC,STL,WTL和Visual C+运行库中使用到的所有的字符串类型。说明各个类的用法,如何构造对象,如何进行类转换等等。Nish为本文提供了Visual C+ 7的managed string 类的用法。阅读本文之前,应完全理解本指南第一部分中阐述的字符类型和编码。字符串类的首要原则:不要随便使用类型强制转换,除非转换的类型是明确由文档规定的。之所以撰写字符串指南这二篇文章,是因为常有人问到如何将X类型的字符串转换到Z类型。提问者使用了强制类型转换(cast),但不知道为什么不能转换成功。各种各样的字符串类型,特别是BSTR,在任何场合都不是三言二语可以讲清的。因此,我以为这些提问者是想让强制类型转换来处理一切。除非明确规定了转换算子,不要将任何其它类型数据强制转换为string。一个字符串不能用强制类型转换到string类。例如:void SomeFunc ( LPCWSTR widestr );main() SomeFunc ( (LPCWSTR) C:foo.txt ); / 错!这段代码100%错误。它可以通过编译,因为类型强制转换超越了编译器的类型检验。但是,能够通过编译,并不证明代码是正确的。下面,我将指出什么时候用类型强制转换是合理的。C语言字符串与类型定义如指南的第一部分所述,Windows API定义了TCHAR术语。它可用于MBCS或Unicode编码字符,取决于预处理设置为_MBCS 或 _UNICODE标记。关于TCHAR的详细说明请阅指南的第一部分。为便于叙述,下面给出字符类型定义:Type Meaning WCHARUnicode character (wchar_t) TCHARMBCS or Unicode character, depending on preprocessor settings LPSTRstring of char (char*) LPCSTRconstant string of char (constchar*) LPWSTRstring of WCHAR (WCHAR*) LPCWSTRconstant string of WCHAR (const WCHAR*) LPTSTRstring of TCHAR (TCHAR*) LPCTSTRconstant string of TCHAR (const TCHAR*) 另外还有一个字符类型OLECHAR。这是一种对象链接与嵌入的数据类型(比如嵌入Word文档)。这个类型通常定义为wchar_t。如果将预处理设置定义为OLE2ANSI,OLECHAR将被定义为char类型。现在已经不再定义OLE2ANSI(它只在MFC 3以前版本中使用),所以我将OLECHAR作为Unicode字符处理。下面是与OLECHAR相关的类型定义:Type Meaning OLECHARUnicode character (wchar_t) LPOLESTRstring of OLECHAR (OLECHAR*) LPCOLESTRconstant string of OLECHAR (const OLECHAR*) 还有以下二个宏让相同的代码能够适用于MBCS和Unicode编码:Type Meaning _T(x)Prepends L to the literal in Unicode builds. OLESTR(x)Prepends L to the literal to make it an LPCOLESTR.宏_T有几种形式,功能都相同。如: TEXT, _TEXT, _TEXT, 和 _T这四种宏的功能相同。COM中的字符串 BSTR 与 VARIANT许多COM接口使用BSTR声明字符串。BSTR有一些缺陷,所以我在这里让它独立成章。BSTR是Pascal类型字符串(字符串长度值显式地与数据存放在一起)和C类型字符串(字符串长度必须通过寻找到结尾零字符来计算)的混合型字符串。BSTR属于Unicode字符串,字符串中预置了字符串长度值,并且用一个零字符来结尾。下面是一个Bob的BSTR字符串:注意,字符串长度值是一个DWORD类型值,给出字符串的字节长度,但不包括结尾零。在上例,Bob含有3个Unicode字符(不计结尾零),6个字节长。因为明确给出了字符串长度,所以当BSTR数据在不同的处理器和计算机之间传送时,COM库能够知道应该传送的数据量。附带说一下,BSTR可以包含任何数据块,不单是字符。它甚至可以包容内嵌零字符数据。这些不在本文讨论范围。C+中的BSTR变量其实就是指向字符串首字符的指针。BSTR是这样定义的:typedef OLECHAR* BSTR;这个定义很糟糕,因为事实上BSTR与Unicode字符串不一样。有了这个类型定义,就越过了类型检查,可以混合使用LPOLESTR和BSTR。向一个需要LPCOLESTR (或 LPCWSTR)类型数据的函数传递BSTR数据是安全的,反之则不然。所以要清楚了解函数所需的字符串类型,并向函数传递正确类型的字符串。要知道为什么向一个需要BSTR类型数据的函数传递LPCWSTR类型数据是不安全的,就别忘了BSTR必须在字符串开头的四个字节保留字符串长度值。但LPCWSTR字符串中没有这个值。当其它的处理过程(如Word)要寻找BSTR的长度值时就会找到一堆垃圾或堆栈中的其它数据或其它随机数据。这就导致方法失效,当长度值太大时将导致崩溃。许多应用接口都使用BSTR,但都用到二个最重要的函数来构造和析构BSTR。就是SysAllocString()和SysFreeString()函数。SysAllocString()将Unicode字符串拷贝到BSTR,SysFreeString()释放BSTR。示例如下:BSTR bstr = NULL;bstr = SysAllocString ( LHi Bob! );if ( NULL = bstr ) / 内存溢出 / 这里使用bstrSysFreeString ( bstr );当然,各种BSTR包装类都会小心地管理内存。自动接口中的另一个数据类型是VARIANT。它用于在无类型语言,诸如JScript,VBScript,以及Visual Basic,之间传递数据。VARIANT可以包容许多不用类型的数据,如long和IDispatch*。如果VARIANT包含一个字符串,这个字符串是BSTR类型。在下文的VARIANT包装类中我还会谈及更多的VARIANT。C+字符串完全指南(2) 各种字符串类- CRT类翻译:连波 20/11/2002 URL: /developer/tech/story/0,2000081602,39098682,00.htm _bstr_t 字符串包装类我已经说明了字符串的各种类型,现在讨论包装类。对于每个包装类,我都会说明它的对象构造过程和如何转换成C类型字符串指针。应用接口的调用,或构造另一个不同类型的字符串类,大多都要用到C类型指针。本文不涉及类的其它操作,如排序和比较等。再强调一下,在完全了解转换结果之前不要随意使用强制类型转换。CRT类_bstr_t_bstr_t 是BSTR的完全包装类。实际上,它隐含了BSTR。它提供多种构造函数,能够处理隐含的C类型字符串。但它本身却不提供BSTR的处理机制,所以不能作为COM方法的输出参数out。如果要用到BSTR* 类型数据,用ATL的CComBSTR类更为方便。_bstr_t 数据可以传递给需要BSTR数据的函数,但必须满足以下三个条件:首先,_bstr_t 具有能够转换为wchar_t*类型数据的函数。其次,根据BSTR定义,使得wchar_t* 和BSTR对于编译器来说是相同的。第三,_bstr_t内部保留的指向内存数据块的指针 wchar_t* 要遵循BSTR格式。满足这些条件,即使没有相应的BSTR转换文档,_bstr_t 也能正常工作。示例如下: / 构造_bstr_t bs1 = char string; / 从LPCSTR构造_bstr_t bs2 = Lwide char string; / 从LPCWSTR构造_bstr_t bs3 = bs1; / 拷贝另一个 _bstr_t_variant_t v = Bob;_bstr_t bs4 = v; / 从一个含有字符串的 _variant_t 构造/ 数据萃取LPCSTR psz1 = bs1; / 自动转换到MBCS字符串LPCSTR psz2 = (LPCSTR) bs1; / cast OK, 同上LPCWSTR pwsz1 = bs1; / 返回内部的Unicode字符串LPCWSTR pwsz2 = (LPCWSTR) bs1; / cast OK, 同上BSTR bstr = bs1.copy(); / 拷贝bs1, 返回BSTR/ . SysFreeString ( bstr );注意,_bstr_t 也可以转换为char* 和 wchar_t*。这是个设计问题。虽然char* 和 wchar_t*不是常量指针,但不能用于修改字符串,因为可能会打破内部BSTR结构。_variant_t _variant_t _variant_t 是VARIANT的完全包装类。它提供多种构造函数和数据转换函数。本文仅讨论与字符串有关的操作。/ 构造_variant_t v1 = char string; / 从LPCSTR 构造_variant_t v2 = Lwide char string; / 从LPCWSTR 构造_bstr_t bs1 = Bob;_variant_t v3 = bs1; / 拷贝一个 _bstr_t 对象/ 数据萃取_bstr_t bs2 = v1; / 从VARIANT中提取BSTR_bstr_t bs3 = (_bstr_t) v1; / cast OK, 同上注意,_variant_t 方法在转换失败时会抛出异常,所以要准备用catch 捕捉_com_error异常。另外要注意 _variant_t 不能直接转换成MBCS字符串。要建立一个过渡的_bstr_t 变量,用其它提供转换Unicode到MBCS的类函数,或ATL转换宏来转换。与_bstr_t 不同,_variant_t 数据可以作为参数直接传送给COM方法。_variant_t 继承了VARIANT类型,所以在需要使用VARIANT的地方使用_variant_t 是C+语言规则允许的。C+字符串完全指南(2) STL和ATL类翻译:连波 21/11/2002 URL: /developer/tech/story/0,2000081602,39098845,00.htm STL类 STL类STL只有一个字符串类,即basic_string。basic_string管理一个零结尾的字符数组。字符类型由模板参数决定。通常,basic_string被处理为不透明对象。可以获得一个只读指针来访问缓冲区,但写操作都是由basic_string的成员函数进行的。basic_string预定义了二个特例:string,含有char类型字符;which,含有wchar_t类型字符。没有内建的TCHAR特例,可用下面的代码实现:/ 特例化typedef basic_string tstring; / TCHAR字符串/ 构造string str = char string; / 从LPCSTR构造wstring wstr = Lwide char string; / 从LPCWSTR构造tstring tstr = _T(TCHAR string); / 从LPCTSTR构造/ 数据萃取LPCSTR psz = str.c_str(); / 指向str缓冲区的只读指针LPCWSTR pwsz = wstr.c_str(); / 指向wstr缓冲区的只读指针LPCTSTR ptsz = tstr.c_str(); / 指向tstr缓冲区的只读指针与_bstr_t 不同,basic_string不能在字符集之间进行转换。但是如果一个构造函数接受相应的字符类型,可以将由c_str()返回的指针传递给这个构造函数。例如:/ 从basic_string构造_bstr_t_bstr_t bs1 = str.c_str(); / 从LPCSTR构造 _bstr_t_bstr_t bs2 = wstr.c_str(); / 从LPCWSTR构造 _bstr_tATL类CComBSTRCComBSTR 是ATL的BSTR包装类。某些情况下比_bstr_t 更有用。最主要的是,CComBSTR允许操作隐含BSTR。就是说,传递一个CComBSTR对象给COM方法时,CComBSTR对象会自动管理BSTR内存。例如,要调用下面的接口函数:/ 简单接口struct IStuff : public IUnknown / 略去COM程序. STDMETHOD(SetText)(BSTR bsText); STDMETHOD(GetText)(BSTR* pbsText);CComBSTR 有一个BSTR操作方法,能将BSTR直接传递给SetText()。还有一个引用操作(operator &)方法,返回BSTR*,将BSTR*传递给需要它的有关函数。CComBSTR bs1;CComBSTR bs2 = new text;pStuff-GetText ( &bs1 ); / ok, 取得内部BSTR地址 pStuff-SetText ( bs2 ); / ok, 调用BSTR转换 pStuff-SetText ( (BSTR) bs2 ); / cast ok, 同上CComVariant CComBSTR有类似于 _bstr_t 的构造函数。但没有内建MBCS字符串的转换函数。可以调用ATL宏进行转换。/ 构造CComBSTR bs1 = char string; / 从LPCSTR构造CComBSTR bs2 = Lwide char string; / 从LPCWSTR构造CComBSTR bs3 = bs1; / 拷贝CComBSTRCComBSTR bs4;bs4.LoadString ( IDS_SOME_STR ); / 从字符串表加载/ 数据萃取BSTR bstr1 = bs1; / 返回内部BSTR,但不可修改!BSTR bstr2 = (BSTR) bs1; / cast ok, 同上BSTR bstr3 = bs1.Copy(); / 拷贝bs1, 返回BSTRBSTR bstr4;bstr4 = bs1.Detach(); / bs1不再管理它的BSTR/ .SysFreeString ( bstr3 );SysFreeString ( bstr4 );上面的最后一个示例用到了Detach()方法。该方法调用后,CComBSTR对象就不再管理它的BSTR或其相应内存。所以bstr4就必须调用SysFreeString()。最后讨论一下引用操作符(operator &)。它的超越使得有些STL集合(如list)不能直接使用CComBSTR。在集合上使用引用操作返回指向包容类的指针。但是在CComBSTR上使用引用操作,返回的是BSTR*,不是CComBSTR*。不过可以用ATL的CAdapt类来解决这个问题。例如,要建立一个CComBSTR的队列,可以声明为: std:list bstr_list;CAdapt 提供集合所需的操作,是隐含于代码的。这时使用bstr_list 就象在操作一个CComBSTR队列。CComVariantCComVariant 是VARIANT的包装类。但与 _variant_t 不同,它的VARIANT不是隐含的,可以直接操作类里的VARIANT成员。CComVariant 提供多种构造函数和多类型操作。这里只介绍与字符串有关的操作。/ 构造CComVariant v1 = char string; / 从LPCSTR构造CComVariant v2 = Lwide char string; / 从LPCWSTR构造CComBSTR bs1 = BSTR bob;CComVariant v3 = (BSTR) bs1; / 从BSTR拷贝/ 数据萃取CComBSTR bs2 = v1.bstrVal; / 从VARIANT提取BSTR跟_variant_t 不同,CComVariant没有不同VARIANT类型之间的转换操作。必须直接操作VARIANT成员,并确定该VARIANT的类型无误。调用ChangeType()方法可将CComVariant数据转换为BSTR。CComVariant v4 = . / 从某种类型初始化 v4CComBSTR bs3;if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ) bs3 = v4.bstrVal;跟 _variant_t 一样,CComVariant不能直接转换为MBCS字符串。要建立一个过渡的_bstr_t 变量,用其它提供转换Unicode到MBCS的类函数,或ATL转换宏来转换。ATL转换宏 ATL转换宏ATL的字符串转换宏可以方便地转换不同编码的字符,用在函数中很有效。宏按照source type2new type 或 source type2Cnew type格式命名。后者转换为一个常量指针 (名字内含C)。类型缩写如下:A:MBCS字符串,char* (A for ANSI) W:Unicode字符串,wchar_t* (W for wide) T:TCHAR字符串,TCHAR* OLE:OLECHAR字符串,OLECHAR* (实际等于W) BSTR:BSTR (只用于目的类型)例如,W2A() 将Unicode字符串转换为MBCS字符串,T2CW()将TCHAR字符串转换为Unicode字符串常量。要使用宏转换,程序中要包含atlconv.h头文件。可以在非ATL程序中使用宏转换,因为头文件不依赖其它的ATL,也不需要 _Module全局变量。如在函数中使用转换宏,在函数起始处先写上USES_CONVERSION宏。它表明某些局部变量由宏控制使用。转换得到的结果字符串,只要不是BSTR,都存储在堆栈中。如果要在函数外使用这些字符串,就要将这些字符串拷贝到其它的字符串类。如果结果是BSTR,内存不会自动释放,因此必须将返回值分配给一个BSTR变量或BSTR的包装类,以避免内存泄露。下面是若干宏转换示例:/ 带有字符串的函数:void Foo ( LPCWSTR wstr );void Bar ( BSTR bstr );/ 返回字符串的函数:void Baz ( BSTR* pbstr );#include main()using std:string;USES_CONVERSION; / 声明局部变量由宏控制使用/ 示例1:送一个MBCS字符串到Foo()LPCSTR psz1 = Bob;string str1 = Bob;Foo ( A2CW(psz1) ); Foo ( A2CW(str1.c_str() );/ 示例2:将MBCS字符串和Unicode字符串送到Bar()LPCSTR psz2 = Bob;LPCWSTR wsz = LBob;BSTR bs1;CComBSTR bs2;bs1 = A2BSTR(psz2); / 创建 BSTR bs2.Attach ( W2BSTR(wsz) ); / 同上,分配到CComBSTRBar ( bs1 ); Bar ( bs2 );SysFreeString
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025短租租赁合同:教育培训场地租赁协议
- 2025年典当合同范本:汽车典当贷款操作细则
- 2025年轨道交通信号电缆项目采购合同
- 2025店长聘用协议:商业地产店长岗位竞聘标准
- 2025年度水利工程水泵安装与防冻合同
- 2025年康复医疗服务体系优化与运营模式创新策略研究报告
- 2025年度图书馆图书采购与读者服务合同
- 2025版企业办公设备维修与保养服务合同
- 2025年新能源汽车专用停车位买卖合同
- 2025版闲置土地居间服务合同
- 电力系统调度运行继电人员继电保护竞赛试题及答案汇编
- 电力行业防汛应急预案演练脚本(2篇)
- 2025 耳鼻喉科鼻息肉术后换药查房操作课件
- 航空航天检测技术
- 初级魔方社团课件
- 储油储气项目社会稳定风险评估报告
- 《RWA 技术规范》标准草案
- 庭院围墙整治方案(3篇)
- 2025年高考物理真题完全解读(广西卷)
- 教师课件的制作培训
- 质量成本控制与管理考核试卷
评论
0/150
提交评论