Windows via C/C++ 学习(5)Windows中使用的Unicode和ANSI函数

来源:互联网 发布:嘉兴南湖行知小学 编辑:程序博客网 时间:2024/05/15 18:09

http://www.cnblogs.com/Fly-pig/archive/2011/01/11/1932954.html


Windows 给出的带有字符串参数的函数都有两个相同版本的函数与之对应,一个是 Unicode 版本,一个是 ANSI 版本,分别以 FunctionNameW 和 FunctionNameA 表示,FunctionName 实际上是一个宏定义。

用户在使用 FunctionNameA 版本的 ANSI 函数时,WIndows 内部会分配一块内存来将 ANSI 字符串转换为 Unicode 字符串,然后再调用 FunctionNameW 版本的 Unicode 函数,FunctionNameW 返回时,FunctionNameA 将释放申请的内存,又会将 Unicode 字符串转换回 ANSI 字符串。所以使用 ANSI 版本的例程会有性能损失。

某些 WIndows API 函数没有 Unicode 版本,如 WinExec 和 OpenFile,这些函数的存在是为了和之前的16位程序兼容,现在可以完成抛开这些函数而使用新的其它的 Unicode 函数。

所有 COM 接口方法必须使用 Unicode 字符串,因为 COM 通常用于不同组件之间的通信。

编译器会将所有的资源编译为 Unicode,如果应用程序没有定义 UNICODE 宏,系统会执行内部转换,将 Unicode 资源转换为 ANSI 资源。

C 运行时库使用的 Unicode 和 ANSI 函数

同 Windows 函数一样,C 运行时库也有 Unicode 和 ANSI 两个版本的函数,但是不象 Windows 函数,ANSI 版本的函数不会在内部将字符或字符串转换为 Unicode。

C 运行时库使用 _UNICODE 定义 Unicode 宏,常用 C 运行时字符操作库的定义可以参考 tchar.h 文件。

在C 运行库中使用安全的字符串函数

象 strcpy 和 wcscpy 之类的 C 运行时函数最大的问题是它不知道缓冲区的大小,它可能导致缓冲区溢出,不能将错误报告给用户。可以使用 Microsoft 提供的安全的字符串函数,这些函数在 strsafe.h 中定义。有关使用安全字符串的主主题,请参考 MSDN(http://msdn.microsoft.com/zh-cn/library/ms647466.aspx),所有 C 运行时函数的可替换安全版本请参考 MSDN (http://msdn.microsoft.com/zh-cn/library/wd3wzwts(VS.80).aspx)。

一些新的安全字符串函数

为了使用新的安全的字符串函数,必须将 strsafe.h 头文件在其它所有头文件之后声明。使用已废弃的字符串函数时编译器会给出警告。

每一个已存在的函数,如 _tcscpy 或 _tcscat,都有一个新的安全的字符串函数与之对应,这些函数的名称一般是在原字符串函数之后增加一个“_s”后缀,表示这是一个安全的字符串函数,如 _tcscpy_s 或 _tcscat_s 等。

如果函数检测到错误,函数会设置 C 运行时变量 errno,函数会返回一个 errno_t 类型的值表示成功或失败,实际上,如果发生错误,永远不会返回一个错误,在调试阶段,会弹出一个表示错误的窗口然后程序终止,在 release 阶段,程序会直接自动终止。

C 运行时允许用户提供一个自己的函数,当前它检测一个无效的参数时,调用这个函数,这个函数有如下形式:

1void InvalidParameterHandler(PCTSTR expression, PCTSTR function, PCTSTR file, unsigned int line, uintptr_t /*pReserved*/);

这个函数在 reslase 时所有参数都为 NULL。

为了使用这个函数,必须调用 _set_invalid_paramter_handler 来注册上面的函数,然后调用 _CrtSetReportModel(_CRT_ASSERT, 0); 来取消所有由 C 运行时触发的所有断言对话框。

除了新的安全字符串函数外,C 运行时还提供了一些新的字符串函数,它们提供了处理字符串的更多的控制。这些函数同样提供了 ANSI (A)和 Unicode (W)两种版本。这些函数有如下形式:

1HRESULT StringCchXXX(Ex)();
2HRESULT StringCbXXX(Ex)();

函数中的 Cch 表示字符数量(Count of characters),使用 _countof 宏来测量缓冲区大小;Cb 表示以字节计算的缓冲区大小(Count of bytes),使用 sizeof 运算符计算缓冲区大小。

这些函数的返回值都是 HRESULT 类型,StringCchXxxEx 或 StringCbXxxEx 函数提供了额外的三个参数,使用户有更多的控制。

Windows 字符串函数

ShlwApi.h 定义了很多有用的字符串函数来格式化与操作系统有关的数值类型,如 StrFormatKBSize 和 StrFormatByteSize 等,Windows Shell 字符串函数参照 MSDN(http://msdn.microsoft.com/zh-cn/library/ms538658.aspx)。

使用字符和字符串的建议

1. 将文本字符串看做是字符数组,而不是看作 char 或 byte 类型的数组;

2. 使用范型数据类型(如 TCHAR/PTSTR)声明字符或字符串;

3. 对字节、字节指针和数据缓冲区使用显式的数据类型(如 BYTE 和 PBYTE);

4. 对字符和字符串字面量使用 TEXT 或 _T 宏声明,但要避免混用,使其有较好的可读性;

5. 进行全局替换(比如将 PSTR 替换为 PTSTR);

6. 修正字符串算法问题。比如,一个函数期望传递一个缓冲区字符数量,而不是字节数量,你必须使用 _countof(szBuffer) 而不是使用 sizeof(szBuffer) 进行计算传递的大小。如果你需要给一个字符串分配一块内存,必须使用 malloc(nCharaters * sizeof(TCHAR)) 而不是使用 malloc(nCharaters),为了避免这个问题,可以定义一个宏如下:

1#define chmalloc(nCharacters) (TCHAR*)malloc(nCharaters * sizeof(TCHAR))

7. 避免使用 printf 族的函数,特别是不要使用 %s 和 %S 在 ANSI 和 Unicode 字符串之间进行转换,应该使用 MultiByteToWideChar 和 WideCharToMultiByte 函数进行转换;

8. 总是同时使用 UNICODE 和 _UNICODE 符号或都不使用。

对于字符串函数的使用,应遵循以下方针:

1. 总是使用如 _s 后缀或 StringCch 前缀的安全的字符串函数,使用后者进行显式地字符串截断处理;

2. 不要使用不安全的 C 运行时库字符串操作函数;

3. 利用 /GS 和 /RTCS 编辑器标志自动地检测缓冲区溢出;

4. 不要使用 Kernel32 方法操作字符串,比如 lstrcat 和 lstrcpy;

5. 在我们要比较的代码中有两种字符串,一种是文件名称、路径、XML 元素和属性、注册表键值等标题性的字符串,这类字符串使用 CompareStringOrdinal 来进行比较,这个方法比较快并且不涉及用户所在地域;另一类是出现在用户界面的字符串,这类字符串要考虑用户地域,所以要使用 CompareString(Ex) 进行比较。

Unicode 和 ANSI 字符串之间的转换

MultiByteToWideChar 函数将多字节字符串转换为宽字符串,定义如下:

1int MultiByteToWideChar(
2    UINT    uCodePage,
3    DWORD   dwFlags,
4    PCSTR   pMultiByteStr,
5    int cbMultiByte,
6    PWSTR   pWideCharStr,
7    int cchWideChar);

通常情况下,一般使用以下步骤来转换一个字符串:

1. 调用 MultiByteToWideChar,将参数 pWideCharStr 设置为 NULL,将 cchWideChar 设置为 0,将 cbMultiByte 设置为 -1(自动计算源字符串长度);

2. MultiByteToWideChar 函数返回一个表示目标字符串大小的整数,这个数值包含了字符串末尾的空字符,使用这个数值为目标字符串分配一个空间;

3. 再次调用 MultiByteToWideChar,这次传递相应的参数;

4. 使用转换的字符串;

5. 释放分配的空间。

WideCharToMultiByte 函数转换一个宽字符串为它的多字节等效字符串,定义如下:

1int WideCharToMultiByte(
2    UINT    uCodePage,
3    DWORD   dwFlags,
4    PCWSTR  pWideCharStr,
5    int cchWideChar,
6    PSTR    pMultiByteStr,
7    int cbMultiByte,
8    PCSTR   pDefaultChar,
9    PBOOL   pfUsedDefaultChar);

判断文本是 ANSI 还是 Unicode

1BOOL IsTextUnicode(const PVOID pvBuffer, int cb, PINT pResult);