错误处理

今天就来了解下windows下的函数是如何进行错误处理的。

调用函数时,它会先验证我们传入的参数,再执行任务。下面是常见的windows函数返回值数据类型。

Microsoft为我们编辑了一个列表,其中列出了各种错误代码及其对应的32位的编号,方便我们理解函数为什么会调用失败。

在内部,当windows函数检测到错误时,它会使用“线程本地存储区”(thread-local storage)将相应的错误代码与“主调线程”绑定到一起。它使得不同线程独立运行,并不会干扰对方的错误代码的情况。函数返回值会指出已发生一个错误。要想查看具体的错误,需要调用GetLastError函数。eg:

DWORD GetLastError();

其作用就一个,即返回由上一个函数调用设置的线程的32位错误代码。

这还不够,我们还需要将其转化为更有用的信息。WinError.h文件里包含了Microsoft定义的错误代码列表。如:

可以看到,每个错误都有三种表示:一个消息ID(可以在源代码中使用的宏,用于与GetLastError的返回值进行比较)、消息文本和一个编号。

 

一些windows函数调用成功可能源于不同的原因,而应用程序或许需要知道成功的原因。为返回这种信息,Microsoft选择采用last error code机制。即可以用GetLastError来确定额外信息。所以,GetLastError的返回值可以表示错误信息,也可以表示成功信息,这要看对应的消息ID了。

 

当我们调试程序时,对线程的GetLastError进行监视是很有必要的。在VS中,Microsoft的调试器提供watch窗口,可以帮助我们。具体的做法是:在watch窗口中选择一行,输入$err,hr。来看个例子,下图。我已经调用了CreateFile函数,其返回值为INVALID_HANDLE_VALUE(-1)的一个HANDLE,指出无法打开文件。但是,watch窗口指出,GetLastError的返回值为0x00000002。多亏了,hr限定符,watch窗口进一步指出错误代码2是“The system cannot find the file specified."这就是消息文本。

VS还搭载了一个很小的实用程序,名为Error Lookup。利用它,可以将消息ID转化为消息文本。如:

如果我希望程序向用户显示错误文本,而不是一个苦逼的ID,那么可以利用这个函数FormatMessage:

DWORD FormatMessage(
    DWORD dwFlags,
    LPCVOID pSource,
    DWORD dwMessageId,
    DWORD dwLanguageId,
    PTSTR pszBuffer,
    DWORD nSize,
    va_list *Arguments);

其功能很不错,可以支持多种语言,从其参数dwLanguageId就可以看出来。其用法后面有示例。

 

定义自己的错误代码

在我们自定义的函数里,我们可以为其自行设置错误代码。为了指出错误,我们只需设置线程的上一个错误代码,然后令自己的函数返回FALSE或其他合适的值。为了设置线程的上一个错误代码,只需调用SetLastError函数,并传递我们认为合适的任何32位值。

VOID SetLastError(DWORD dwErrCode);

我会尽量使用WinError.h中现有的代码。也可自己创建自己的代码。错误代码是一个32位数,由下表字段组成。

就目前来说,我们只需要注意一个字段---第29位。注意:Facility字段很大,足以容纳4096个可能的值。其中,前256个值为Microsoft保留的,其余值我们随意设定。

 

ErrorShow示例程序

这个应用程序演示了如何得到一个错误代码的文本描述。简单来说,它展示了调试器的watch窗口和Error Lookup程序是如何工作的。启动程序时,将出现下面窗口:

输入错误代码ID,单击Lookup按钮,即可以看到消息文本。下面截取其源代码中FormatMessage的调用部分:

上述代码中,第一行从编辑控件获取错误代码。然后,指向一个内存块的句柄被实例化并初始化为NULL。FormatMessage函数在内部分配一块内存,并返回指向该内存的句柄。调用FormatMessage时,我向它传入了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage:我们希望获得与一个系统定义的错误代码对应的字符串。另外,还传入了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,要求该函数分配一块足以容纳错误文本描述的内存。此块内存的句柄将在hlocal变量中返回。FORMAT_MESSAGE_IGNORE_INSERTS标志则允许我们获得含有%占位符的消息,这些占位符将被windows用来提供更多上下文相关信息。第三个参数指出想要查找的错误代码,第四个参数指出语言选择。由于我们对windows本身所提供的信息感兴趣,所以将根据两个特定的常量(即LANG_NEUTRAL和SUBLANG_NEUTRAL)来生成语言标识符,这两个常量联合到一起将生成一个0值--即操作系统默认语言。

 

我们可以向自己的模块中添加错误代码,具体做法是使用Message Compiler(MC.exe)来创建一个消息资源并将其添加到DLL或exe模块中。VS的Error Lookup工具允许我们使用Modules对话框来完成这个操作。

posted @ 2015-09-12 18:17  jiu~  阅读(348)  评论(0编辑  收藏  举报