Windows 核心编程:2.2

2Unicode续

Windows中的Unicode函数和ANSI函数

[@more@]

Windows中的Unicode函数和ANSI函数

前面已经讲过,有两个函数称为CreateWindowsEx,一个CreateWindowsEx接受Unicode字符

串,另一个CreateWindowsEx接受ANSI字符串。情况确实如此,CreateWindowsExW是接受Unicode字符串的函数版本。函数名结尾处的大写字母W是英文wide()的缩写。每个Unicode字符的长度是16位,因此,它们常常称为宽字符。CreateWindowsExA的结尾处的大写字母A表示该函数可以接受ANSI字符串。但是,在我们的代码中,通常只包含了对CreateWindowsEx的调用,而不是直接调用CreateWindowsEx W或者CreateWindowsEx A。在WinUser. h文件中,CreateWindowsEx实际上是定义为下面这种形式的一个宏:

#ifdef UNICODE

#define CreateWindowEX CreateWindowsExW

#else

#define CreateWindowsEX CreaterWindowsExA

#endif

当编译源代码模块时, UNICODE 是否已经作了定义,将决定你调用的是哪个CreateWindowsEx版本。当转用一个16位的Windows应用程序时,你在编译期间可能没有定义UNICODE。对CreateWindowsEx函数的任何调用都会将该宏扩展为对CreateWindowsExA的调用,即对CreateWindowsExANSI版本的调用。由于16Windows只提供了CreateWindows E xANSI版本,因此可以比较容易地转用它的应用程序。

Windows XP下,MicrosoftCreateWindowsEx A源代码只不过是一个形实替换程序层或翻译层,用于分配内存,以便将ANSI字符串转换成Unicode字符串。该代码然后调用CreateWindowExW,并传递转换后的字符串。当CreateWindowsExW返回时,CreateWindowsExA便释放它的内存缓存,并将窗口句柄返回给你。如果要创建其他软件开发人员将要使用的动态链接库(DLL),请考虑使用下面的方法。在DLL中提供两个输出函数。一个是ANSI版本,另一个是Unicode版本。在ANSI版本中,只需要分配内存,执行必要的字符串转换,并调用该函数的Unicode版本(本章后面部分介绍这个进程)。

Windows API中的某些函数,比如WinExecOpenFile等,只是为了实现与16Windows程序的向后兼容而存在,因此,应该避免使用。应该使用对CreateProcessCreateFile函数的调用来取代对WinExecOpenFile函数的调用。从系统内部来讲,老的函数完全可以调用新的函数。

老的函数存在的一个大问题是,它们不接受Unicode字符串。当调用这些函数时,必须传递ANSI字符串。另一方面,所有新的和未过时的函数在Windows XP中都同时拥有ANSIUnicode两个版本。

2.8.4 Windows字符串函数

Windows还提供了一组范围很广的字符串操作函数。这些函数与C运行期字符串函数(如strcpywcscpy)很相似。但是该操作系统函数是操作系统的一个组成部分,操作系统的许多组件都使用这些函数,而不使用C运行期库。建议最好使用操作系统函数,而不要使用C运行

期字符串函数。这将有助于稍稍提高你的应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在你的应用程序运行时,它们可能已经被装入R A M。若要使用这些函数,系统必须运行Windows XPWindows Vista。如果安装了Internet Explorer 5.0或更新的版本,也可以在较早的Windows版本中获得这些函数。在经典的操作系统函数样式中,操作系统字符串函数名既包含大写字母,也包含小写字母,它的形式类似这个样子: StrCat,StrChr,StrCmpStrCpy等。若要使用这些函数,必须加上ShlWApi.h头文件。另外,如前所述,这些字符串函数既有ANSI版本,也有Unicode版本,例

StrCat AStrCatW。由于这些函数属于操作系统函数,因此,当创建应用程序时,如果定义了UNICODE(不带前置下划线),那么它们的符号将扩展为宽字符版本

2.9 成为符合ANSIUnicode的应用程序

即使你不打算立即使用Unicode,最好也应该着手将你的应用程序转换成符合Unicode的应用程序。下面是应该遵循的一些基本原则:

将文本串视为字符数组,而不是c h a r s数组或字节数组。

将通用数据类型(如TCHARPTSTR)用于文本字符和字符串。

将显式数据类型(如BYTEPBYTE)用于字节、字节指针和数据缓存。

TEXT宏用于原义字符和字符串。

执行全局性替换(例如用PTSTR替换PSTR)。

修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。

这意味着你不应该传递sizeof(szBuffer ) ,而应该传递( s i z e o f ( s z B u ff e r ) / s i z e o f ( T C H A R )。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用malloc(nCharacters *sizeof(TCHAR)), 而不是调用m a l l o c( n C h a r a c t e r s )。在上面所说的所有原则中,这是最难记住的一条原则,如果操作错误,编译器将不发出任何警告。

当我为本书的第一版编写示例程序时,我编写的原始程序只能编译为ANSI程序。后来,当我开始撰写本章的内容时,我想我应该鼓励使用Unicode,并且打算创建一些示例程序,以便展示你可以非常容易地编写既可以用Unicode也可以用ANSI来编译的程序。这时我发现最好

的办法是将本书的所有示例程序进行转换,使它们都能够用UnicodeANSI进行编译。我用了大约4个小时将所有程序进行了转换。考虑到我以前从来没有这方面的转换经验,这个速度是相当不错了。

2.9.1 Windows字符串函数

Windows也提供了一组用于对Unicode字符串进行操作的函数:

Lstrcat 将一个字符串置于另一个字符串的结尾处

Lstrcmp 对两个字符串进行区分大小写的比较

Lstrcmpi 对两个字符串进行不区分大小写的比较

Lstrcpy 将一个字符串拷贝到内存中的另一个位置

Lstrlen 返回字符串的长度(按字符数来计量)

这些函数是作为宏来实现的,这些宏既可以调用函数的Unicode版本,也可以调用函数的ANSI版本,这要根据编译源代码模块时是否已经定义了UNICODE而定。例如,如果没有定义UNICODEl s t r c a t函数将扩展为l s t r c a t A。如果定义了UNICODEl s t r c a t将扩展为l s t r c a t W。有两个字符串函数,即lstrcmplstrcmpi,它们的行为特性与等价的C运行期函数是不同的。C运行期函数s t r c m ps t r c m p iw c s c m pw c s c m p i只是对字符串中的代码点的值进行比较,这就是说,这些函数将忽略实际字符的含义,只是将第一个字符串中的每个字符的数值与第二个字符串中的字符的数值进行比较。而Windows函数lstrcmplstrcmpi是作为对Windows函数CompareString的调用来实现的。该函数对两个Unicode字符串进行比较。CompareString的第一个参数用于设定语言IDLCID),这是个3 2位值,用于标识一种特定的语言。CompareString使用这个LCID来比较这两个字符串,方法是对照一种特定的语言来查看它们的字符的含义。这种操作方法比C运行期函数简单地进行数值比较更有意义。

lstrcmp函数系列中的任何一个函数调用CompareString时,该函数便将调用WindowsGetThreadString函数的结果作为第一个参数来传递:

LCID GetThreadLocal();

每次创建一个线程时,它就被赋予一种语言。函数将返回该线程的当前语言设置。CompareString的第二个参数用于标识一些标志,这些标志用来修改该函数比较两个字符串时所用的方法。

2-5 Compare String 的标志及含义:

N O R M _ I G N O R E C A S E 忽略字母的大小写

N O R M _ I G N O R E K A N AT Y P E 不区分平假名与片假名字符

N O R M _ I G N O R E N O N S PA C E 忽略无间隔字符

N O R M _ I G N O R E S Y M B O L S 忽略符号

N O R M _ I G N O R E W I D T H 不区分单字节字符与作为双字节字符的同一个字符

S O RT _ S T R I N G S O RT 将标点符号作为普通符号来处理

lstrcmp调用CompareString时,它传递0作为f d w S t y l e的参数。但是,当lstrcmpi调用CompareString时,它就传递N O R M _ I G N O R E C A S ECompareString的其余4个参数用于设定两个字符串和它们各自的长度。如果为cch1参数传递- 1,那么该函数将认为p S t r i n g 1字符串是以0结尾,并计算该字符串的长度。对于p S t r i n g 2字符串来说,参数c c h 2的作用也是一样。其他C运行期函数没有为Unicode字符串的操作提供很好的支持。例如, t o l o w e rt o u p p e r函数无法正确地转换带有重音符号的字符。为了弥补C运行期库中的这些不足,必须调用下面这些Windows函数,以便转换Unicode字符串的大小写字母。这些函数也可以正确地用于ANSI字符串。

头两个函数:

PTSTR CharLower(PTSTR pszString);

PTSTR CharUpper(PTSTR pszString);

既可以转换单个字符,也可以转换以0结尾的整个字符串。若要转换整个字符串,只需要传递字符串的地址即可。若要转换单个字符,必须像下面这样传递各个字符:

TCHAR cLowerCaseChar = CharLower((PSTR) szString[0]);

将单个字符转换成一个PTSTR,便可调用该函数,将一个值传递给它,在这个值中,较低的1 6位包含了该字符,较高的1 6位包含0。当该函数看到较高位是0时,该函数就知道你想要转换单个字符,而不是整个字符串。返回的值是个3 2位值,较低的1 6位中是已经转换的字符。

下面两个函数与前面两个函数很相似,差别在于它们用于转换缓存中包含的字符(该缓存不必以0结尾):

DWORD CharLowerBuffer (PTSTR pszString, DWORD cchSting);

DWORD CharUpperBuffer (PTSTR pszString, DWORD cchSting);

其他的C运行期函数,如isalphaislowerisupper,返回一个值,指明某个字符是字母字符、小写字母还是大写字母。Windows API 提供了一些函数,也能返回这些信息,但是Windows函数也要考虑用户在控制面板中指定的语言:printf函数家族是要介绍的最后一组C运行期函数。如果在定义了_ UNICODE的情况下编译你的源代码模块,那么p r i n t f函数家族便希望所有字符和字符串参数代表Unicode字符和字符串。但是,如果在没有定义_ UNICODE的情况下编译你的源代码模块, p r i n t f函数家族便希望传递给它的所有字符和字符串都是ANSI字符和字符串。Microsoft公司已经给C运行期的p r i n t f函数家族增加了一些特殊的域类型。其中有些域类型尚未被ANSI C采用。新类型使你能够很容易地对ANSIUnicode字符和字符串进行混合和匹配。操作系统的w s p r i n t f函数也得到了增强。下面是一些例子(请注意大写S和小写s的使用):

Char szA[100];

WCHAR szW[100];

sprint(szA, “%s”, “Ansi Str”);

//Conver Unicode string to ANSI:

Sprint(szA, “%s”, L“Unicode Str”);

//Conver Unicode string to Unicode:

Sprint(szA, “%s”, L“Unicode Str”);

//ALL Unicode:

swprint(szW, L“%s”, L“Unicode Str”);

//Conver Ansi to Unicode:

swprint(szW, L“%s”, “ANSI Str”);

2.9.2 资源

当资源编译器对你的所有资源进行编译时,输出文件是资源的二进制文件。资源(字符串表、对话框模板和菜单等)中的字符串值总是写作Unicode字符串。在Windows XPWindows2 0 0 0下,如果应用程序没有定义UNICODE宏,那么系统就会进行内部转换。例如,如果在编译源代码模块时没有定义UNICODE,调用L o a d S t r i n g实际上就是调用L o a d S t r i n g A函数。这时L o a d S t r i n g A就从你的资源中读取字符串,并将该字符串转换成ANSI字符串。ANSI形式的字符串将从该函数返回给你的应用程序。

2.9.3 确定文本是ANSI文本还是Unicode文本

实际上, Microsoft公司自己的大多数产品并没有配备任何Unicode文本文件。但是预计将来这种情况是会改变的(尽管这需要一个很长的

过程)。当然,Windows XPN o t e p a d (记事本)应用程序允许你既能打开Unicode文件,也能打开ANSI文件,并且可以创建这些文件。

对于许多用来打开文本文件和处理这些文件的应用程序(如编译器)来说,打开一个文件后,应用程序就能方便地确定该文本文件是包含ANSI字符还是Unicode字符。IsTe x t Unicode函数能够帮助进行这种区分:

DWORD IsTextUnicode(CONST PVOID pwBuffer, int cb, PINT pResult);

文本文件存在的问题是,它们的内容没有严格和明确的规则,因此很难确定该文件是包含ANSI字符还是Unicode字符。IsTe x t Unicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此I s Te x t Unicode有可能返回不正确的结果。第一个参数pvBuffer用于标识要测试的缓存的地址。该数据是个无效指针,因为你不知道你拥有的是ANSI字符数组还是Unicode字符数组。第二个参数cb用于设定pvBuffer指向的字节数。同样,由于你不知道缓存中放的是什么,因此cb是个字节数,而不是字符数。请注意,不必设定缓存的整个长度。当然, IsTex Unicode能够测试的字节越多,得到的结果越准确。第三个参数pResult是个整数的地址,必须在调用I s Te x t Unicode之前对它进行初始化。对该整数进行初始化后,就可以指明你要I s Te x t Unicode执行哪些测试。也可以为该参数传递N U L L

在这种情况下,I s Te x t Unicode将执行它能够进行的所有测试(详细说明请参见Platform SDK文档)。如果I s Te x t Unicode认为缓存包含Unicode文本,便返回T R U E,否则返回FA L S E。确实是这样,尽管Microsoft将该函数的原型规定为返回DWORD,但是它实际上返回一个布尔值。如果在p R e s u l t参数指向的整数中必须进行特定的测试,该函数就会在返回之前设定整数中的信息位,以反映每个测试的结果。

1 7章中的Flie Rev示例应用程序演示了IsTextUnicode 函数的使用。

2.9.4 UnicodeANSI之间转换字符串

Windows函数MulitByteToWideChar用于将多字节字符串转换成宽字符串。下面显示了MulitByteToWideChar函数。

Int MultiByteToWideChar(

UINT uCodePage,

DWORD dwFlags,

PCSTR pMultiByteStr,

Int cchMultiByte,

PWSTR pWideCharStr;

Int cchWideChar;

);

u C o d e P a g e参数用于标识一个与多字节字符串相关的代码页号。

d w F l a g s参数用于设定另一个控件,它可以用重音符号之类的区分标记来影响字符。这些标志通常并不使用,在d w F l a g s参数中传递0

p M u l t i B y t e S t r参数用于设定要转换的字符串, c c h M u l t i B y t e参数用于指明该字符串的长度(按字节计算)。如果为c c h M u l t i B y t e参数传递- 1,那么该函数用于确定源字符串的长度。转换后产生的Unicode版本字符串将被写入内存中的缓存,其地址由p Wi d e C h a r S t r参数指定。必须在c c h Wi d e C h a r参数中设定该缓存的最大值(以字符为计量单位)。如果调用MulitByteToWideChar,给c c h Wi d e C h a r参数传递0,那么该参数将不执行字符串的转换,而是返回为使转换取得成功所需要的缓存的值。一般来说,可以通过下列步骤将多字节字符串转换成Unicode等价字符串:

1) 调用MulitByteToWideChar函数,为p Wi d e C h a r S t r参数传递N U L L,为c c h Wi d e C h a r参数传递0

2) 分配足够的内存块,用于存放转换后的Unicode字符串。该内存块的大小由前面对M u l t B y t e To Wi d e C h a r的调用返回。

3) 再次调用MulitByteToWideChar,这次将缓存的地址作为p Wi d e C h a r S t r参数来传递,并传递第一次调用MulitByteToWideChar时返回的缓存大小,作为c c h Wi d e c h a r参数。

4) 使用转换后的字符串。

5) 释放Unicode字符串占用的内存块。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/16916129/viewspace-1015079/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/16916129/viewspace-1015079/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值