📄 windows核心编程1-8章.txt
字号:
Microsoft公司为Unicode设计了Windows API,这样,它可以尽量减少对你的代码的影响。实际上,你可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。你只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。
2. 8.1 C运行期库对Unicode的支持
为了利用Unicode字符串,因此定义了一些数据类型。标准的C头文件String.h已经作了修改,以便定义一个名字为wchar_t的数据类型,它是一个Unicode字符的数据类型:
见原书P23的程序(1)
例如,如果你想要创建一个缓存,用于存放最多为99个字符的Unicode字符串和一个结尾为零的字符,你可以使用下面这个语句:
见原书P23的程序(2)
该语句创建了一个由100个16位值组成的数组。当然,标准的C运行期字符串函数,如strcpy、strchr和strcat等,只能对ANSI字符串进行操作,它们不能正确地处理Unicode字符串。因此,ANSI C也拥有一组补充函数。图2-1显示了一些标准的ANSI C字符串函数,后面是它们的等价Unicode函数。
图2-1 标准的ANSI C字符串函数和它们的等价Unicode函数
见原书P23的程序(3)和P24的程序
请注意,所有的Unicode函数均以wcs开头,wcs是宽字符串的英文缩写。若要调用Unicode函数,只需用前缀wcs来取代任何ANSI字符串函数的前缀str即可。
说明 大多数软件开发人员可能已经不记得这样一个非常重要的问题了,那就
是Microsoft公司提供的C运行期库与ANSI的标准C运行期库是一致的。
ANSI C规定,C运行期库支持Unicode字符和字符串。这意味着你始终都可
以调用C运行期函数,以便对Unicode字符和字符串进行操作,即使你是在
Windows 98上运行,也可以调用这些函数。换句话说,wcscat,wcslen和wcstok
等函数都能够在Windows 98上很好地运行,这些都是你必须关心的操作系统函数。
对于包含了对str函数或wcs函数进行显式调用的代码来说,你无法非常容易地同时为ANSI和Unicode对这些代码进行编译。在本章前面部分的内容中,我说过可以创建同时为ANSI和Unicode进行编译的单个源代码文件。若要建立这种双重功能,你必须包含Tchar.h文件,而不是包含String.h文件。
Tchar.h文件的唯一作用是帮助你创建ANSI/Unicode通用源代码文件。它包含你应该用在源代码中的一组宏,而不应该直接调用str函数或者wcs函数。如果你在编译源代码文件时定义了_UNICODE,这些宏就会引用wcs这组函数。如果你没有定义_UNICODE,那么这些宏将引用str这组宏。
例如,在Tchar.h中有一个宏称为_tcscpy。如果在你包含该头文件时没有定义_UNICODE,那么_tcscpy就会扩展为ANSI的strcpy函数。但是如果定义了_UNICODE,_tcscpy将扩展为Unicode的wcscpy函数。拥有字符串参数的所有C运行期函数都在Tchar.h文件中定义了一个通用宏。如果你使用通用宏,而不是ANSI/Unicode的特定函数名,你就能够顺利地创建可以为ANSI或Unicode进行编译的源代码。
但是,除了使用这些宏之外,还有一些操作你是必须进行的。Tchar.h文件包含了一些其他的宏。
若要定义一个ANSI/Unicode通用的字符串数组,请使用下面的TCHAR数据类型。如果定义了_UNICODE,TCHAR将声明为下面的形式:
见原书P25的程序(1)
如果没有定义_UNICODE,则TCHAR将声明为下面的形式:
见原书P25的程序(2)
使用该数据类型,你可以象下面这样分配一个字符串:
见原书P25的程序(3)
你也可以创建对字符串的指针:
见原书P25的程序(4)
不过上面这行代码存在一个问题。按照默认设置,Microsoft公司的C++编译器能够编译所有的字符串,就象它们是ANSI字符串,而不是Unicode字符串。因此,如果没有定义_UNICODE,该编译器将能正确地编译这一行代码。但是,如果定义了_UNICODE,就会产生一个错误。若要生成一个Unicode字符串而不是ANSI字符串,你必须将该代码行改写为下面的样子:
见原书P25的程序(5)
原义字符串前面的大写字母L,用于告诉编译器该字符串应该作为Unicode字符串来编译。当编译器将字符串置于程序的数据部分中时,它在每个字符之间分散插入零字节。这种变更带来的问题是,现在只有当定义了_UNICODE时,程序才能成功地进行编译。我们需要另一个宏,以便有选择地在原义字符串的前面加上大写字母L。这项工作由_TEXT宏来完成,_TEXT宏也在Tchar.h文件中做了定义。如果定义了_UNICODE,那么_TEXT定义为下面的形式:
见原书P25的程序(6)
如果没有定义_UNICODE,_TEXT将定义为
见原书P25的程序(7)
使用该宏,我们可以改写上面这行代码,这样,无论是否定义了_UNICODE宏,它都能够正确地进行编译。如下所示:
见原书P26的程序(1)
_TEXT宏也可以用于原义字符。例如,若要检查一个字符串的第一个字符是否是大写字母L,只需编写下面的代码即可:
见原书P26的程序(2)
2. 8.2 Windows定义的Unicode数据类型
Windows头文件定义了下面这个表列出的数据类型。
数据类型 说明
WCHAR Unicode字符
PWSTR 指向Unicode字符串的指针
PCWSTR 指向一个恒定的Unicode字符串的指针
这些数据类型总是指Unicode字符和字符串。Windows头文件也定义了ANSI/Unicode通用数据类型PTSTR和PCTSTR。这些数据类型既可以指ANSI字符串,也可以指Unicode字符串,这取决于当你编译程序模块时是否定义了UNICODE宏。
请注意,这里的UNICODE宏没有前置的下划线。_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当你编译源代码模块时,通常你必须同时定义这两个宏。
2. 8.3 Windows中的Unicode函数和ANSI函数
我在前面已经讲过,有两个函数称为CreateWindowEx。一个CreateWindowEx接受Unicode字符串,另一个CreateWindowEx接受ANSI字符串。情况确实如此,不过,这两个函数的原型实际上是下面的样子:
见原书P27的程序(1)
CreateWindowExW是接受Unicode字符串的函数版本。函数名结尾处的大写字母W是英文wide(宽)的缩写。每个Unicode字符的长度是16位,因此它们常常称为宽字符。CreateWindowExA的结尾处的大写字母A表示该函数可以接受ANSI字符串。
但是,在我们的代码中,我们通常只包含了对CreateWindowEx的调用,而不是直接调用CreateWindowExW或者CreateWindowExA。在WinUser.h文件中,CreateWindowEx实际上是定义为下面这种形式的一个宏
见原书P27的程序(2)
当你编译源代码模块时,UNICODE是否已经作了定义,这将决定你调用的是哪个CreateWindowEx版本。当你转用一个16位的Windows应用程序时,你在编译期间可能没有定义UNICODE。你对CreateWindowEx函数的任何调用都会将该宏扩展为对CreateWindowExA的调用,即对CreateWindowEx的ANSI版本的调用。由于16位Windows只提供了CreateWindowsEx的ANSI版本,因此你可以比较容易地转用它的应用程序。
在Windows 2000下,Microsoft的CreateWindowExA源代码只不过是一个形实替换程序层或翻译层,用于分配内存,以便将ANSI字符串转换成Unicode字符串,然后该代码调用CreateWindowExW,并传递转换后的字符串。当CreateWindowEXW返回时,CreateWindowExA便释放它的内存缓存,并将窗口句柄返回给你。
如果你要创建其他软件开发人员将要使用的动态连接库(DLL),请考虑使用下面的方法。在DLL中提供两个输出函数。一个是ANSI版本,另一个是Unicode版本。在ANSI版本中,只需要分配内存,执行必要的字符串转换,并调用该函数的Unicode版本。(我将在本章的后面部分中介绍这个进程。)
在Windows 98下,Microsoft的CreateWindowExA源代码是执行操作的函数。Windows 98提供了到达接受Unicode参数的所有Windows函数的进入点,但是这些函数并不将Unicode字符串转换成ANSI字符串,它们只返回运行失败的消息。调用GetLastError将返回ERROR_CALL_NOT_IMPLEMENTED。这些函数中只有ANSI版本的函数才能正确地运行。如果你编译的代码调用了任何宽字符函数,你的应用程序将无法在Windows 98下运行。
Windows API中的某些函数,比如WinExec和OpenFile等,只是为了实现与16位Windows程序的向后兼容而存在的,因此应该避免使用。你应该使用对CreateProcess和CreateFile函数的调用来取代对WinExec和OpenFile函数的调用。从系统内部来讲,老的函数完全可以调用新的函数。老的函数存在的一个大问题是,它们不接受Unicode字符串。当你调用这些函数时,你必须传递ANSI字符串。另一方面,所有新的和未过时的函数在Windows 2000中都同时拥有ANSI和Unicode两个版本。
2. 8.4 Windows字符串函数
Windows还提供了一组范围很广的字符串操作函数。这些函数与C运行期字符串函数(如strcpy和wcscpy)很相似。但是该操作系统函数是操作系统的一个组成部分,操作系统的许多组件都使用这些函数,而不使用C运行期库。我建议你最好使用操作系统函数,而不要使用C运行期字符串函数。这将有助于稍稍提高你的应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序所使用,比如操作系统的外壳进程Explorer.exe来使用。由于这些函数使用得很多,因此在你的应用程序运行时,它们可能已经被装入RAM。
若要使用这些函数,系统必须运行Windows 2000或Windows 98。如果你安装了Internet Explorer 4.0或更新的版本,你也可以在较早的Windows版本中获得这些函数。
在经典的操作系统函数样式中,操作系统字符串函数名既包含大写字母,也包含小写字母,它的形式类似这个样子:比如StrCat,StrChr,StrCmp,和StrCpy等。若要使用这些函数,你必须加上ShlWApi.h头文件。另外,正如前面所述,这些字符串函数既有ANSI版本,也有Unicode版本,例如StrCatA和StrCatW。由于这些函数属于操作系统函数,因此,当你创建应用程序时,如果定义了UNICODE(不带前置下划线),那么它们的符号将扩展为它们的宽字符版本。
2. 9 使你的应用程序成为符合ANSI和Unicode的应用程序
即使你不打算立即使用Unicode,最好也应该着手将你的应用程序转换成符合Unicode的应用程序。下面是你应该遵循的一些基本原则:
* 将文本串视为字符数组,而不是chars数组或字节数组。
* 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。
* 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。
* 将TEXT宏用于原义字符和字符串。
* 执行全局性替换。(例如用PTSTR替换PSTR。)
* 修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。这意味着你不应该传递sizelf(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果你需要为字符串分配一个内存块,并且你拥有该字符串中的字符数目,那么请记住你要按字节来分配内存。这就是说,你应该调用malloc(nCharacters * sizeof(TCHAR)),而不是调用malloc(nCharacters)。在上面所说的所有原则中,这是最难记住的一条原则,如果你操作错误,编译器将不发出任何警告。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -