一、常见错误
调用windows函数时,会先验证参数,在开始执行任务,如果参数传入错误,或者由于其他原因导致操作无法执行,则函数的返回值将指出函数函数因为哪些原因失败,大多数的返回值类型
数据类型 | 指出函数调用失败的原因 |
---|---|
VOID | 这个函数不可能失败。只有极少数的函数的返回值是void |
BOOL | 失败返回0;否则返回非0值。在使用时应避免测试返回值是否为true;应该去判断返回值是否是FALSE |
HANDLE | 失败NULL;否则,HANDLE将标识一个可操作的对象,其中当返回值是INVALID_HANDLE_VALUE(-1)这个句柄时也是失败的,但是NULL与它所代表的的含义完全不同 |
PVOID | 失败NULL;否则,PVOID 将标识一个数据块的内存地址 |
LONG/DWORD | 返回计数器的函数通常会返回一个LONG或DWORD。如果函数因为某种原因不能对我们想要记数的东西进行计数,他通常会返回0或-1 |
自己以后设计函数时一定要对返回值进行慎用,尤其是返回值为void的函数,代表该函数不会出错。
二、错误代码
microsoft有一个列表,其中列出了所有可能的函数返回的错误代码,并且为每个代码分配了一个32位的编号。
机制: 当windows函数检测到错误时,它会使用一种名为“线程本地存储区”的机制将相应的错误代码与“主调线程”关联到一起,使得不同的线程能独立运行,不会相互干扰对方的错误代码,且函数返回错误时,会返回一个已经发生了的错误代码,使用GetLastError()可以查看具体错误。
DWORD GetLastError() //返回由上一个函数调用设置的线程的32位错误代码
当windows函数调用失败之后应当立刻调用GetLastError,因为假如又调用了另一个windows函数,则此时GetLastError函数的返回值可能会被改写。如果windows函数调用成功,返回值可能会变为ERROR_SUCCESS.
为什么要采用GetLastError?
Windows函数调用成功的原因是不同的,例如,创建一个具名事件内核对象时,以下两种情况都会成功:对象实际完成创建,或存在一个同名的事件内核对象。应用程序或许知道这些原因,为捕捉到具体的返回信息,Microsoft采用“上一个错误代码”机制,所以当特定函数调用成功时,可以使用GetLastError函数来获取确定的额外的信息。例如:CreateEvent函数,如果已经存在具名事件,就返回ERROR_ALREAD_EXISTS。
windows 32位的错误代码定义列表在 WinError.h头文件之中。下面摘录部分
//
// MessageId: ERROR_INVALID_FUNCTION
//
// MessageText:
//
// Incorrect function.
//
#define ERROR_INVALID_FUNCTION 1L // dderror
//
// MessageId: ERROR_FILE_NOT_FOUND
//
// MessageText:
//
// The system cannot find the file specified.
//
#define ERROR_FILE_NOT_FOUND 2L
//
// MessageId: ERROR_PATH_NOT_FOUND
//
// MessageText:
//
// The system cannot find the path specified.
//
#define ERROR_PATH_NOT_FOUND 3L
三、错误代码的表示
右上面所摘录的部分WinError.h头文件的内容可以看出,一个错误有三种表示:一个消息ID(可以在资源代码中使用的宏,用于与GetLastError的返回值进行对比)、消息文本(描述错误的具体内容的英文文本)、一个编号(不要使用此编号,使用ID)
Windows中有一个函数,可以将错误代码转换为相应的文本描述。此函数为FormatMessage
DWORD FormatMessage(
_In_ DWORD dwFlags,
_In_opt_ LPCVOID lpSource,
_In_ DWORD dwMessageId,
_In_ DWORD dwLanguageId,
_Out_ LPWSTR lpBuffer,
_In_ DWORD nSize,
_In_opt_ va_list *Arguments);
该函数的具体使用方式以及各个参数的详细解释:FormatMessage FormatMessage函数的用法
FormatMessage的功能十分丰富,用来格式化消息字符串,就是处理消息资源的。它可以支持多种语言(自然语言,汉语,英语等)。它能获取一个语言标识作为参数,并返回那种语言的文本,当然,我们必须先翻译字符串,并将翻译好的消息表资源嵌入自己的.exe或dll模块之中。
四、定义自己的错误代码
Microsoft允许我们将Windows函数如何向其调用者指出错误这种机制使用于我么自己的函数中,假定我们要写一个供其他人调用的函数。这个函数可能会因为某些原因失败,所以需要向调用者指出错误。
为了指出错误,只需要设置线程的上一个错误代码,然后令自己的函数返回FALSE,INVALID_HANDLE_VALUE、NULL或者其他适合的值。为了设置线程的上一个错误代码,只需要调用以下函数,并传递我们认为合适的任何32位的值:
VOID SetLastError(_In_ DWORD dwErrCode);
五、错误代码的字段组成
错误代码是一个32位的数,下表描述了错误代码的几个不同字段所代表的的不同含义
位 | 31-30 | 29 | 28 | 27-16 | 15-0 |
---|---|---|---|---|---|
内容 | 严重性 | Microsoft | Facility | 异常代码 | |
含义 | 0 = 成功 1 = 信息(提示) 2 = 警告 3 = 错误 | 0 = Microsoft 定义的代码 1 = 客户定义的代码 | 必须为 0 | 前256个值由Microsoft保留 | Microsoft / 客户定义的代码 |
29位的存在可以保证我们的错误代码绝对不会与Microsoft现在和将来定义的错误代码冲突。Facility字段非常大,足以容纳4096个可能的值,其中前256个是Microsoft保留的,其余的值可由自己来定义。