第一部分,必备知识

第1章 错误处理

概述: 当调用Windows函数失败时,必须正确处理错误代码以确定失败原因。Windows通过函数返回值指示错误,并通过线程局部存储机制保存详细的错误代码供后续查询。

Windows函数的错误指示方式

//Windows函数通过各种返回值类型指示失败,不同数据类型有不同的失败指示方式

//1. VOID类型:极少见,表示函数不可能失败
VOID ExitProcess(UINT uExitCode); //该函数从不返回,因此无失败指示

//2. BOOL类型:失败返回0(FALSE),成功返回非0
BOOL CloseHandle(HANDLE hObject);
HANDLE hFile = CreateFile(...);
if (!CloseHandle(hFile)) { //检查是否失败,避免直接测试==TRUE
DWORD dwError = GetLastError(); //获取详细错误代码
//错误处理...
}

//3. HANDLE类型:失败返回NULL或INVALID_HANDLE_VALUE(-1)
HANDLE hFile = CreateFile(
TEXT("test.txt"),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) { //CreateFile特有,不是返回NULL
DWORD dwError = GetLastError();
//错误处理...
}

//4. PVOID类型:失败返回NULL,成功返回内存地址
PVOID pvMem = VirtualAlloc(NULL, 1024, MEM_RESERVE, PAGE_READWRITE);
if (pvMem == NULL) {
DWORD dwError = GetLastError();
//错误处理...
}

//5. LONG/DWORD类型:较复杂,需查阅文档
DWORD dwNum = GetCurrentProcessId(); //该函数不会失败,返回进程ID

获取详细错误信息:GetLastError与FormatMessage

//当Windows函数失败后,应立即调用GetLastError获取错误代码
DWORD GetLastError();

//使用FormatMessage将错误代码转换为可读文本描述
void ErrorExit(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);

//输出错误信息
printf("Function: %s\nError: %s\n", lpszFunction, (LPCTSTR)lpMsgBuf);

LocalFree(lpMsgBuf);
ExitProcess(dw);
}

/*
GetLastError关键要点:
1. 通过线程局部存储(Thread-local storage)获取当前线程的错误代码
2. 必须在函数失败后立即调用,否则可能被后续Windows函数调用覆盖
3. 即使函数成功,也可能返回额外信息(如CreateEvent成功时可能返回ERROR_ALREADY_EXISTS)
4. 函数成功时通常返回ERROR_SUCCESS
*/

错误代码的位域结构与自定义错误

//错误代码是32位整数,其位域结构如下:
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---+-+-+-----------------------+-------------------------------+
// |Sev|C|R| Facility | Code |
// +---+-+-+-----------------------+-------------------------------+
//
// 第29位:区分错误来源
// 0 = Microsoft定义的错误代码
// 1 = 用户自定义错误代码

//设置自定义错误代码供调用者获取
void MyFunction() {
//...某些操作失败
SetLastError(ERROR_MY_CUSTOM_ERROR); //在WinError.h中定义或自定义
}

//使用宏定义而非数字值,WinError.h中定义了所有系统错误代码
//如:ERROR_FILE_NOT_FOUND, ERROR_ACCESS_DENIED, ERROR_OUTOFMEMORY等

ErrorShow示例程序:错误代码查询工具

/*
ErrorShow程序演示如何:
1. 查询系统错误代码的文本描述
2. 加载特定DLL(如netmsg.dll)查询模块特定错误
3. 使用Visual Studio调试技巧:在监视窗口中使用$err,hr查看GetLastError返回值

程序功能:
- 输入错误代码编号(十进制或十六进制)
- 显示对应的错误描述字符串
- 支持从系统或指定DLL模块获取错误信息
*/

总结:

永远不要忽略Windows函数的返回值,必须检查是否成功。不同函数的错误指示方式不同,需查阅SDK文档确认(如CreateFile失败返回INVALID_HANDLE_VALUE而非NULL)。错误代码具有线程局部性,每个线程维护自己的最后错误代码。使用GetLastError获取详细错误信息,使用FormatMessage转换为可读文本。使用WinError.h中的宏定义而非数字值表示错误代码。