Chapter1 : 错误处理
WinError.h包含了一个微软的错误编码列表
一个Widnows错误由三个部分组成:
- MessageID :一个宏定义与Number联系,一般用这个来和GetLastError进行比较
- Message Text: 错误内容
- Number: 错误号码,尽量避免错误号码,使用MessageID

当Windows函数出错时,应该尽快使用GetLastError()获得错误编号,不能可能会被后续的Windows 函数的结果覆盖。
一些Windows函数可能因为多种原因成功,比如试图创建一个named event kernel object ,在成功创建对象和系统中已经存在相同命名的对象时都会返回成功。
在使用VS调试时,在Watch 中添加 $err,hr 。可以实时查看当前的Error Code和对应的文本信息。
一个Error Code由32bit组成。

第29位定义了,当前ErrorCode是否由微软定义,如果是自己定义的Error Code这一位必须为0
Facility Code有4096种可能值,头256位由微软保留使用。
Error Code可以通过FormatMessage函数,获得对应的Message Text.典型程序如下
1 |
DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); |
2 |
|
3 |
|
4 |
BOOL fOk = FormatMessage( |
5 |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | |
6 |
FORMAT_MESSAGE_ALLOCATE_BUFFER, |
7 |
NULL, dwError, systemLocale, |
8 |
(PTSTR) &hlocal, 0, NULL); |
9 |
|
10 |
if (!fOk) { |
11 |
|
12 |
HMODULE hDll = LoadLibraryEx(TEXT(“netmsg.dll”), NULL, |
13 |
DONT_RESOLVE_DLL_REFERENCES); |
14 |
|
15 |
if (hDll != NULL) { |
16 |
fOk = FormatMessage( |
17 |
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS | |
18 |
FORMAT_MESSAGE_ALLOCATE_BUFFER, |
19 |
hDll, dwError, systemLocale, |
20 |
(PTSTR) &hlocal, 0, NULL); |
21 |
FreeLibrary(hDll); |
22 |
} |
23 |
} |
DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); 当前语言设置
FORMAT_MESSAGE_FROM_SYSTEM 表示处理来自Windows 官方系统的错误代码
FORMAT_MESSAGE_ALLOCATE_BUFFER 为Message Text分配足够的空间
FORMAT_MESSAGE_IGNORE_INSERTS 获得的消息带有%占位符
如果FormatMessage无法从Widnows错误列表中获得信息,也可以从其他的DLL中获得对应的错误信息。
Chapter 2:字符和字符串处理
最好将本地的非Unicode字符集,转为Unicode字符集。这有利于本地化处理,并帮助与COM和.NET的交互。
在最初字符序列都是以单byte的以\0结尾的字符序列构成。但是单byte最多只能表示256个字符,对于一些国家远远不够。
后续推出了double-byte character sets(DBCSs)(双字节字符集)。在双字节字符集中每个字符由1个或2个 byte组成。
以日报汉字为例如果第一个字符在0x81-0x9F或者0xE0-0xFC,就必须查看第二个字符来确认这个汉字。在现在可以吧DBCSs丢了,来使用Unicode。
在Windows Vista中每个Unicode字符使用UTF-16编码,UTF是Unicode Transformation Forat的简称。UTF-16将每个字符编码为2btye长也就是16bit。在这本书中谈论Unicode默认就是值Unicode-16.
16bit不够表示所有字符,对于这些字符UTF-16支持代理,通过使用4byts来表示一个字符。
.NET对于所有字符使用UTF-16编码。
UTF-8:将字符编码为1-4byte长度,这里感觉类似于多字节编码
UTF-32: 将所有字节编码为4byte
ANSI 和 Unicode字符
微软使用char 和 whar_t 定义 ANSI字符和Unicode字符。现在的VS默认项目都是Unicode编码。
对于Unicode字符的使用如下
通过在字符序列前增加L,来定义Unicode字符。
1 |
|
2 |
wchar_t c = L‘A’; |
3 |
|
4 |
wchar_t szBuffer[100] = L“A String” |
一些微软定义的Unicode和ANSI的宏如下
typedef char CHAR; // An 8-bit character
typedef wchar_t WCHAR; // A 16-bit character
// Pointer to 8-bit character(s)
typedef CHAR *PCHAR;
typedef CHAR *PSTR;
typedef CONST CHAR *PCSTR
// Pointer to 16-bit character(s)
typedef WCHAR *PWCHAR;
typedef WCHAR *PWSTR;
typedef CONST WCHAR *PCWSTR;
微软还定义了TCHAR 以及对应的宏实现了,无论编译器是否定义了UNICODE宏都不会起冲突。对应宏如下。
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST WCHAR *PCTSTR;
#define __TEXT(quote) quote
#define __TEXT(quote) L##quote
#else
typedef CHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST CHAR *PCTSTR;
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)
在VS中如果使用PSTR定义就是多字节字符集、使用PWSTR定义就是Unicode字符集。

Windows中的Unicode 和 ANSI 函数
从Windows NT 开始,所有的Widnows 版本都使用Unicode构建。如果你传递ANSI字符给Windows 函数,系统会自动转换为Unicode集后再给系统。
如果函数的返回值是ANSI字符集,系统会自动将Unicode字符转化为ANSI再给你。转化的过程对于用户都是不可见的。
对于需要字符串作为参数的Windows函数都会提供两个版本,一个接收Unicode字符串,一个接收ANSI字符串。
比如CreateWindowsExW和CreateWindowsExA。同时Windows还提供了一个通用版本CreateWindowsEx,
它是一个宏,如果定义了Unicode,就是CreateWindowExW,否则是CreateWindowExA。
对于现在的系统来说,ExA的函数就是多了一个转换层,将ANSI转化为Unicode后再传递给ExW。
推荐使用Unicode字符串以及ExW函数,在转换的过程中可能会出现一些Bug,同时还会有性能和内存的损耗。
一些Windows函数例如WinExec和OpenFile,存在的唯一理由就是与老版本的Windows兼容。
需要使用CreateProcess和CreateFile来替代它。
在COM中所有的字符串都以Unicode形式存在。编译器编译的所有资源文件到二进制中都是以UNICODE形式存在
例如字符串表、对话框模板、菜单等等。
C中的ANSI、Unicode函数
C中也想Windows函数一样,也提供了两种不同的函数处理ANSI和UNICODE,但是不像Windows函数
在C中两个函数并不会在内部进行转换,如果混用会出错。
可以使用MultiByteToWideChar将多字节字符集转化为UNICODE字符集。
int MultiByteToWideChar(
UINT uCodePage,
DWORD dwFlags,
PCSTR pMultiByteStr,
int cbMultiByte,
PWSTR pWideCharStr,
int cchWideChar);
uCodePage标识了多字节字符集关联的一个代码页值。
dwFlags影响使用了读音符号的字符,一遍不使用,传入0
pMultiByteStr指定要转换的字符串
cbMultiByte指定字符串长度,以byte为单位,如果传入-1,函数自动判断字符串长度
pWideCharStr指定的缓冲区写入,转换完成的Unicode字符集
cchWideChar是指定的写入缓冲区的最大字符数,这里注意是以Character作为单位
如果将0作为参数给cchWideChar,函数返回成功写入缓冲区所需要的最大长度包括\0.
一般按照一下步骤将多字节字符集转化为Unicode字符集
- 调用MultiByteToWideChar函数,在cbMultyByte传入-1, cchWideChar传入0
- 申请一块大小为第一步返回值*sizeof(WCHAR)的内存区域
- 调用MultiByteToWideChar,pWideCharStr为第二步申请的内存,cchWideChar为第一步返回值*sizeof(WCHAR)
WideCharToMultiByte函数将Unicode字符集转化为多字节字符集
int WideCharToMultiByte(
UINT uCodePage,
DWORD dwFlags,
PCWSTR pWideCharStr,
int cchWideChar,
PSTR pMultiByteStr,
int cbMultiByte,
PCSTR pDefaultChar,
PBOOL pfUsedDefaultChar);
前面所有的参数与MultiByteToWideChar类似,不过cbMultiByte以byte作为单位。这也就意味着如果按照之前的转换方法
,第一步返回的长度不需要做乘法。
pDetaultChar,只有当一个字符没有在uCodePage指定的代码页中出现时起作用,将pDefaultChar所指向的字符进行替代。如果该值为NULL,将默认字符为?
pfUsedDefaultChar是个Bool类型的指针,当转换的Unicode字符集出现不能进行转化的情况也就是pDefaultChar生效时,返回True,否则返回False。
判断文本编码字符集
BOOL IsTextUnicode(CONST PVOID pvBuffer, int cb, PINT pResult);
需要注意这个函数不保证精准。
pvBuffer指向待检测的文本地址
cb标识出待检测的文本长度,由于你不知道文本是Unicode还是多字节编码,cb不一定要为待检测文本的全部长度,但是越长IsTextUnicode函数越准确
pRestult指向一个整数,通过该整数的二进制位确认IsTextUnicode进行那些测试,如果传入为NULL,执行所有检测项。
Unicode字符集返回True,反之返回False。如果传入了pResult,对应的pRestul中对应的二进制位也会进行修改,已反应对应的检测项结果