前言
在深入讨论Microsoft Windows提供的诸多特性之前,应该先理解各个Windows函数是如何进行错误处理的。这一篇文章主要讲述怎样定义自己的错误代码。
一、错误的发生,以及常见的错误
调用Windows函数时,它会先验证我们传给他的参数,然后开始执行任务。如果传入的参数无效,或者由于其它原因导致操作无法执行,则函数的返回值将指出函数因为某些原因失败了。
常见的Windows函数返回值数据类型:
- VOID:这个函数不可能失败。
- BOOL:如果函数失败,返回值为0,否则返回一个非0值
- HANDLE:如果函数失败,则返回值通常为NULL,否则,HANDLE将标识一个可以操纵的对象。
- PVOID:如果函数调用失败,则返回值为NULL;否则,PVOID将标识一个数据库的内存地址。
- LONG/DWORD:这种类型比较棘手,暂不讨论。
通常情况下,如果WIndows函数能返回错误代码,将有助于我们理解函数调用为啥会失败。为此,Microsoft编辑了一个列表,其中列出了所有可能的错误代码,并为每个错误代码部分都分配了一个32位的编号。
在内部,当WIndows函数检测到错误时,它会使用一种名为线程本地存储区 thread-local-storage的机制将相应的错误代码与主调线程关联在一起(线程本地存储后面的文章讨论)。这种机制使不同的线程能独立运行,不会出现相互干扰对方的错误代码的情况。函数返回时,其返回值会指出一个易发生的错误,要查看是什么错误,请调用GetLastError()函数。如下所示:
DWORD GetLastError();//返回由上一个函数调用设置的线程的32位错误代码
有了32位错误代码后,可以在WinError.h头文件中查看Microsoft定义的错误代码列表。该头文件太多,无法全部展示。 下面是该头文件的部分代码:
#define NO_ERROR 0
#define ERROR_SUCCESS 0
#define ERROR_INVALID_FUNCTION 1
#define ERROR_FILE_NOT_FOUND 2
#define ERROR_PATH_NOT_FOUND 3
#define ERROR_TOO_MANY_OPEN_FILES 4
#define ERROR_ACCESS_DENIED 5
#define ERROR_INVALID_HANDLE 6
#define ERROR_ARENA_TRASHED 7
#define ERROR_NOT_ENOUGH_MEMORY 8
#define ERROR_INVALID_BLOCK 9
#define ERROR_BAD_ENVIRONMENT 10
#define ERROR_BAD_FORMAT 11
#define ERROR_INVALID_ACCESS 12
#define ERROR_INVALID_DATA 13
#define ERROR_OUTOFMEMORY 14
#define ERROR_INVALID_DRIVE 15
#define ERROR_CURRENT_DIRECTORY 16
#define ERROR_NOT_SAME_DEVICE 17
#define ERROR_NO_MORE_FILES 18
#define ERROR_WRITE_PROTECT 19
#define ERROR_BAD_UNIT 20
#define ERROR_NOT_READY 21
#define ERROR_BAD_COMMAND 22
#define ERROR_CRC 23
#define ERROR_BAD_LENGTH 24
#define ERROR_SEEK 25
#define ERROR_NOT_DOS_DISK 26
#define ERROR_SECTOR_NOT_FOUND 27
#define ERROR_OUT_OF_PAPER 28
#define ERROR_WRITE_FAULT 29
#define ERROR_READ_FAULT 30
#define ERROR_GEN_FAILURE 31
#define ERROR_SHARING_VIOLATION 32
#define ERROR_LOCK_VIOLATION 33
#define ERROR_WRONG_DISK 34
#define ERROR_SHARING_BUFFER_EXCEEDED 36
#define ERROR_HANDLE_EOF 38
#define ERROR_HANDLE_DISK_FULL 39
#define ERROR_NOT_SUPPORTED 50
#define ERROR_REM_NOT_LIST 51
#define ERROR_DUP_NAME 52
#define ERROR_BAD_NETPATH 53
#define ERROR_NETWORK_BUSY 54
#define ERROR_DEV_NOT_EXIST 55
#define ERROR_TOO_MANY_CMDS 56
#define ERROR_ADAP_HDW_ERR 57
#define ERROR_BAD_NET_RESP 58
#define ERROR_UNEXP_NET_ERR 59
#define ERROR_BAD_REM_ADAP 60
#define ERROR_PRINTQ_FULL 61
#define ERROR_NO_SPOOL_SPACE 62
#define ERROR_PRINT_CANCELLED 63
#define ERROR_NETNAME_DELETED 64
#define ERROR_NETWORK_ACCESS_DENIED 65
#define ERROR_BAD_DEV_TYPE 66
#define ERROR_BAD_NET_NAME 67
#define ERROR_TOO_MANY_NAMES 68
#define ERROR_TOO_MANY_SESS 69
#define ERROR_SHARING_PAUSED 70
#define ERROR_REQ_NOT_ACCEP 71
#define ERROR_REDIR_PAUSED 72
#define ERROR_FILE_EXISTS 80
#define ERROR_CANNOT_MAKE 82
#define ERROR_FAIL_I24 83
#define ERROR_OUT_OF_STRUCTURES 84
#define ERROR_ALREADY_ASSIGNED 85
#define ERROR_INVALID_PASSWORD 86
#define ERROR_INVALID_PARAMETER 87
#define ERROR_NET_WRITE_FAULT 88
#define ERROR_NO_PROC_SLOTS 89
#define ERROR_TOO_MANY_SEMAPHORES 100
#define ERROR_EXCL_SEM_ALREADY_OWNED 101
#define ERROR_SEM_IS_SET 102
#define ERROR_TOO_MANY_SEM_REQUESTS 103
#define ERROR_INVALID_AT_INTERRUPT_TIME 104
#define ERROR_SEM_OWNER_DIED 105
要查看WinError.h,请到该网站:WinError.h
函数调用失败之后,应该立马使用GetLastError,否则会被下一个Windows函数的结果改写。注意,成功的调用可能会将该值改写为ERROR_SUCCESS。
二、定义自己的错误代码
前面说了Windows函数如何向其调用者指出错误,MicroSoft还允许将这种机制用于我们自己的函数中。假定我们要写一个供他人调用的函数,这个函数可能会因为某些原因调用失败,所以我们需要向他的调用者指出错误。
为了指出错误,需要设置线程上的一个错误代码,然后令自己的函数返回FALSE,INVALID_HANDLE_VALUE,NULL或者其它合适的值。
为了设置线程的上一个错误代码,只需要调用以下代码,并传递我们认为合适的任何32位值:
VOID SetLastError(DWORD dwErrCode);
错误代码的不同字段:
- 31-30:严重性:0为成功,1为提示,2为警告,3为错误
- 29:Microsoft/客户:0为Microsoft定义的代码,1为客户定义的代码
- 28:保留:必须为0
- 27-16:前256个值由Microsoft保留
- 15-0:异常代码:Microsoft/客户定义的代码