第1章 错误处理
1.1 定义自己的错误代码
前面讲述了Windows函数如何向其调用者指出错误。除此之外,Microsoft还允许将这种机制用于我们自己的函数中。假定我们要写一个供其他人调用的函数。这个函数可能会因为这样或那样的原因而失败,所以需要向调用者指出错误。
为了指出错误,只需设置线程的上一个错误代码,然后令自己的函数返回FALSE, INVALID_HANDLE_VALUE、NULL或者其他合适的值。为了设置线程的上一个错误代码,只需调用以下函数,并传递我们认为合适的任何32位值:
VOID SetLastError(DWORD dwErrCode);
我会尽量使用WinError.h中现有的代码——只要代码能很好地反映我想报告的错误。如果WinError.h中的任何一个代码都不能准确反映一个错误,就可以创建自己的代码。错误代码是一个32位数,由表1-2描述的几个不同的字段组成。
表1-2 错误代码的不同字段
位 内容 含义 31–30 严重性 0 = 成功 1 = 信息(提示) 2 = 警告 3 = 错误 29 Microsoft/客户 0 = Microsoft定义的代码 1 = 客户定义的代码 28 保留 必须为0 27–16 Facility代码 前256个值由Microsoft保留 15–0 异常代码 Microsoft/客户定义的代码 Microsoft这些字段将在第24章详细讨论。就目前来说,唯一需要注意的重要字段是第29位。
承诺,在它所生成的所有错误代码中,此位将始终为0。但是,如果要创建我们自己的错误代码,就必须在此位放入一个1。通过这种方式,可以保证我们的错误代码绝不会与Microsoft现在和将来定义的错误代码冲突。注意,Facility字段非常大,足以容纳4096个可能的值。其中,前256个值是为Microsoft保留的,其余的值可由我们自己的应用程序来定义。
1.2 ErrorShow示例程序
ErrorShow应用程序(01-ErrorShow.exe)演示了如何得到一个错误代码的文本描述。此应用程序的源代码和资源文件可以在本书配套网页的01-ErrorShow目录中找到,网址为http://wintellect.com/Books.aspx。
7
简单地说,这个应用程序展示了调试器的Watch窗口和Error Lookup程序是如何工作的(参见前面的两个屏幕截图)。启动程序时,将出现下图所示窗口。
7
Windows核心编程(第5版)
可以在编辑控件中输入任何错误代码。单击Look Up按钮后,错误的文本描述将在对话框底部的可滚动窗口中显示。对于这个应用程序,我们唯一感兴趣的是如何调用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 a value of 0
DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
// 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(\ 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(\
上述代码中,第一行从编辑控件获取错误代码。然后,指向一个内存块的句柄被实例化并
FormatMessage函数在内部分配一块内存,初始化为NULL。并返回指向这块内存的句柄。
8
调用FormatMessage时,我向它传入了FORMAT_MESSAGE_FROM_SYSTEM标志。
该标志告诉FormatMessage:我们希望获得与一个系统定义的错误代码对应的字符串。另外,还传入了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,要求该函数分配一块足以容纳错误文本描述的内存。此块内存的句柄将在hlocal变量中返回。FORMAT_MESSAGE_IGNORE_INSERTS标志则允许我们获得含有%占位符的消息。这些占位符将被Windows用来提供更多上下文相关信息,如下图所示。
8
第1章 错误处理
如果不传递这个标志,就必须在Arguments参数中提供这些占位符的值。但这对于Error Show程序来说是不可能的,因为消息的内容事先是未知的。
第三个参数指出想要查找的错误代码。第四个参数指出要用什么语言来显示文本描述。由于我们对Windows本身所提供的消息感兴趣,所以将根据两个特定的常量(即LANG_NEUTRAL和SUBLANG_NEUTRAL)来生成语言标识符,这两个常量联合到一起将生成一个0值——即操作系统的默认语言。这种情况下,我们不能硬编码一种特定的语言,因为事先并不知道操作系统的安装语言是什么。
如果FormatMessage成功,文本描述就在这块内存中,我把它复制到对话框底部的可滚动窗口中。如果FormatMessage失败,我会尝试在NetMsg.dll模块中查找消息代码,看错误是否与网络有关(有关如何在磁盘上搜索DLL的详情,请参见第20章)。利用NetMsg.dll模块的句柄,我再一次调用FormatMessage。由此可见,每个DLL(或.exe)都可以有自己的一套错误代码。我们也可以向自己的模块添加错误代码,具体做法是使用Message Compiler(MC.exe)来创建一个消息资源并将其添加到DLL(或.exe)模块中。Visual Studio的Error Lookup工具允许我们使用Modules对话框来完成这个操作。
9
9
相关推荐: