Windows错误码
Windows函数执行前会首先验证传递的参数,如果参数无效或者因为某些原因无法执行,失败原因会通过返回值来指出
数据类型 | 失败的值 |
---|---|
VOID | 不可能失败 |
BOOL | 失败返回0,否则返回非0.尽量检查返回值不为FALSE,而不是是否为TRUE |
HANDLE | 函数失败通常返回NULL,某些函数失败返回INVALID_HANDLE_VALUE,否则,HANDLE将标识一个可以被操作的对象。查询文档来得知返回值 |
PVOID | 调用失败返回NULL,否则PVOID将标识一个数据块的内存地址 |
LONG/DWORD | 返回计数的函数通常返回LONG或者DWORD。如果函数不能对我们想要计数的东西进行计数,就会返回0或-1.需要查阅文档 |
在内部,当Windows函数检测到错误,就会使用“线程本地存储区”的机制将相应的错误码与“主调线程”关联到一起。这种机制保证不同的线程能独立运行,不会出现相互干扰对方错误代码的情况。
函数返回时返回值会指出错误,可以调用API函数来查询:
DWORD GetLastError();
这个函数返回由上一个函数调用设置的线程的32位错误码。我们获取错误码后需要转换为更有用的信息,可以查看WinError.h的头文件,头文件的一部分如下:
WinError.h
//MessageId: ERROR_SUCCESS
//MessageText:
//The operation completed successfully.
#define ERROR_SUCCESS 0L
每个错误都有三种表示:
- 消息ID:一个可以在源代码中使用的宏,用于与GetLastError的返回值进行比较
- 消息文本:描述错误的英文文本
- 编号:避免使用此编号,尽量使用文本表示
Windows函数失败后要马上调用 GetLastError,如果在调用之前又调用了另一个Windows函数,那么此值很可能就被改写了。
在VS中,我们在代码的debug模式下查看watch窗口(监视),在某一行输入$err,hr就可以让它始终显示线程的上一个错误码和错误的描述文本。或者直接使用工具中的错误查找(Error lookup),将错误代码转换为文本。
Windows提供了一个函数,可以将错误代码转换为相应的文本描述,以便我们把错误描述打印出来,而且支持多种自然语言。它获取一个语言标识符作为参数,并返回那种语言的文本。我们应首先翻译字符串,并将翻译好的消息表嵌入到自己的.exe或DLL模块中。之后,这个函数就能自动选择正确的字符串。
DWORD FormatMessage(
DWORD dwFlags,
LPCVOID pSource,
DWORD dwMessageId,
DWORD dwLanguageId,
PTSTR pszBuffer,
DWORD nSize,
va_list *Arguments);
- dwFlags:格式化选项
- FORMAT_MESSAGE_ALLOCATE_BUFFER // 此函数会分配内存以包含描述字串
- FORMAT_MESSAGE_FROM_SYSTEM, // 在系统的id映射表中寻找描述字串
- FORMAT_MESSAGE_FROM_HMODULE // 在其他资源模块中寻找描述字串
- FORMAT_MESSAGE_FROM_STRING // 消息ID是个字串,不是个DWORD
- 通常为:FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
- pSource:消息表资源来自哪里,这个值依靠dwFlags.
- 指定了FORMAT_MESSAGE_FROM_HMODULE的话,此参数表示模块的HANDLE.
- 指定了FORMAT_MESSAGE_FROM_STRING的话,此参数表示id字串.
- 如果这两个标示符都没设置,那么lpSource将会被忽略。
- dwMessageId:所需格式化消息的标识符。当dwFlags设置了FORMAT_MESSAGE_FROM_STRING,这个参数将会被忽略
- dwLanguageId:消息描述所用的语言.通常为:0表示自动选择
- pszBuffer: 一个缓冲区指针来接受格式化后的消息。如果dwFlags没有指定FORMAT_MESSAGE_ALLOCATE_BUFFER,则为自己提供的缓冲区.否则为系统LocalAlloc分配,需要被用户LocalFree
- nSize:如果FORMAT_MESSAGE_ALLOCATE_BUFFER没有设置,那么这个参数指定了输出缓冲区的消息,以TCHAR为单位。如果FORMAT_MESSAGE_ALLOCATE_BUFFER设置了,这个参数设置以TCHARs为单位的输出缓冲区的最小值。这个输出缓冲区不能大于64KB。
- Arguments:通常不使用
定义自己的错误码
Windows可以向调用者指出错误,我们还可以把这种机制用在自己的函数中。我们自己的代码需要被他人调用,如果我们的函数调用失败,就要向他人指出错误。
我们只需要设置线程的上一个错误代码,然后令自己的函数返回FALSE、INVALID_HANDLE_VALUE、NULL或其他合适的值。我们可以调用下面的函数来设置上一个线程的错误代码,并传递我们认为合适的任何32位的值:
VOID SetLastError(DWORD dwErrCode);
只要Windows自己的代码能反映我们的错误,我们就应该用Windows的代码,如果WinError.h中的代码不能反映我们的错误,我们就可以创建自己的代码。错误代码是一个32位的数:
位 | 31-30 | 29 | 28 | 27-16 | 15-0 |
---|---|---|---|---|---|
内容 | 严重性 | Microsift/客户 | 保留 | Facility代码 | 异常代码 |
含义 | 0成功,1提示信息,2警告,3错误 | 0是Microsift的代码,1是客户定义的代码 | 必须是0 | 前256个值由Microsift保留 | Microsift/客户定义的代码 |
我们要注意第29位,我们要自己创建错误码就要将此位设置为1.