第1章 错误处理
标签: 函数设计 返回类型 错误处理
本章内容
1. 1 定义自己的错误代码
1. 2 ErrorShow示例程序
调用Windows函数时,它会先验证我们传给它的参数,然后再开始执行任务。如果传入的参数无效,或者由于其他原因导致操作无法执行,则函数的返回值将指出函数因为某些原因失败了。
数据类型 | 指出函数调用失败的值 |
---|---|
VOID | 这个函数不可能失败。只有极少数Windows函数的返回值类型为VOID |
BOOL | 这个函数失败,返回值为0;否则,返回值是一个非0值。应避免测试返回值是否为TRUE;最稳妥的做法是检查它是否不为FALSE |
HANDLE | 如果函数失败,则返回值通常为NULL;否则,HANDLE将标识一个可以操纵的对象。请注意这种返回值,因为某些函数会返回为INVALID_HANDLE_VALUE的一个句柄值,它被定义为-1。函数的Platform SDK文档清楚说明了函数是返回NULL还是INVALID_HANDLE_VALUE来标识失败 |
PVOID | 如果函数调用失败,则返回值为NULL;否则,PVOID将标识一个数据块的内存地址 |
LONG/DWORD | 这种类型比较棘手。返回计数的函数通常会返回一个LONG或DWORD。如果函数处于某种原因不能对我们想要计数的东西进行比较,它通常会返回0或-1(具体取决于函数)。如果要调用一个返回LONG/DWORD的函数,务必仔细阅读Platform SDK文档,确保我们会正确地检查可能出现的错误 |
谈谈自己的体会:
比如以下代码:
VOID swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
很简单,利用指针实现了两个int类型数据的交换。但是,代码中存在严重的漏洞,如果传入的指针中有NULL值出现,怎么办?显然不符合我们的预期,我们理应暂停交换,然后告知调用者问题的根源。
然后,我们开始更改代码:
BOOL swap(int *a, int *b)
{
if (a == NULL || b == NULL)
{
return FALSE;
}
int temp = *a;
*a = *b;
*b = temp;
}
好了,这段代码已经看起来像那么回事了,但是它还不完美。当传入NULL值时,我们只是知道它失败返回了FALSE,并不知道它到底因为什么原因失败的。
在内部,当Windows函数检测到错误时,它会使用一种名为“线程本地存储区”的机制将相应的错误代码与“主调线程”关联到一起。这种机制使不同的线程能独立运行,不会出现相互干扰对方的错误代码的情况。函数返回时,其返回值会指出已发生一个错误,要查看具体是什么错误,请调用GetLastError函数,如下所示:
DWORD GetLastError();
此函数的作用很简单,就是返回由上一个函数调用设置的线程的32位错误代码。
有了32位错误代码之后,接着需要把它转换为更有用的信息。WinError.h头文件包含了Microsoft定义的错误代码列表。
//
// MessageId: ERROR_SUCCESS
//
// MessageText:
//
// The operation completed successfully.
//
#define ERROR_SUCCESS 0L
#define NO_ERROR 0L // dderror
//
// 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
//
// MessageId: ERROR_TOO_MANY_OPEN_FILES
//
// MessageText:
//
// The system cannot open the file.
//
#define ERROR_TOO_MANY_OPEN_FILES 4L
//
// MessageId: ERROR_ACCESS_DENIED
//
// MessageText:
//
// Access is denied.
//
#define ERROR_ACCESS_DENIED 5L
可以看出,每个错误都有三种表示:一个消息ID、消息文本和一个编号。
GetLastError()可以获取错误代码。当然了,如果想生成向用户显示的字符串,FormatMessage是首选的方式。
1.1 定义自己的错误代码
Microsoft还允许我们在设计供其他人调用的函数时,向调用者指出错误。
int swap(int *a, int *b)
{
if (a == NULL || b == NULL)
{
SetLastError(RPC_X_NULL_REF_POINTER);
return FALSE;
}
int temp = *a;
*a = *b;
*b = temp;
SetLastError(ERROR_SUCCESS);
return TRUE;
}
int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
int *a = NULL;
int b = 3;
int result = swap(a, &b);
DWORD dwErrorId = GetLastError();
return(0);
}
上图可以明显看出,错误代码是0x000006f4(1780L),多亏有了hr限定符,Watch窗口进一步指出错误代码1780L是“向占位程序传递了空的索引指针”。
1.2 ErrorShow示例程序
ErrorShow应用程序(01-ErrorShow.exe)演示了如何得到一个错误代码的文本描述。此应用程序的源代码和资源文件可以在本书配套网页的01-ErrorShow目录中找到,网址为http://www.wintellect.com/devcenter/category/resources。
程序主要利用了FormatMessage生成了错误描述字符串,感兴趣的同学可以自行下载代码阅读。此处我贴下关键代码和演示结果。
// Get the error code
DWORD dwError = GetDlgItemInt(hwnd, IDC_ERRORCODE, NULL, FALSE);
HLOCAL hlocal = NULL; // Buffer that gets the error message string
// Use the default system locale since we look for Windows messages.
// Note: this MAKELANGID combination has 0 as value
DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); // Chinese
//DWORD systemLocale = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); // English
// Get the error code's textual description
BOOL fOk = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, dwError, systemLocale,
(PTSTR) &hlocal, 0, NULL);
if (!fOk) {
// Is it a network-related error?
HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL,
DONT_RESOLVE_DLL_REFERENCES);
if (hDll != NULL) {
fOk = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
hDll, dwError, systemLocale,
(PTSTR) &hlocal, 0, NULL);
FreeLibrary(hDll);
}
}
if (fOk && (hlocal != NULL)) {
SetDlgItemText(hwnd, IDC_ERRORTEXT, (PCTSTR) LocalLock(hlocal));
LocalFree(hlocal);
} else {
SetDlgItemText(hwnd, IDC_ERRORTEXT,
TEXT("No text found for this error number."));
}
小结
1. 第1章主要讲了GetLastError获取错误代码,FormatMessage用来生成错误描述字符串。
2. 新年开始上班,准备从Windows核心编程读书笔记开始,专心写写博客,希望自己能坚持下来。