当我们调用Windows函数时,它会先验证我们传给它的参数,然后开始执行任务。

如果传入的参数无效,或者由于其他原因导致操作无法执行,则函数的返回值将指出函数因为某些原因失败了。

下表暂时了大多数Windows函数使用的返回值的数据类型:

Windows函数错误返回值数据类型

 

在内部,当Windows函数检测到错误时,它会使用一种名为“线程本地存储”的机制将相应的错误代码与“主调线程”关联到一起。这种机制使不同的线程能独立运行,不会出现相互干扰对方的错误代码的情况。

函数返回时,其返回值会指出已发生一个错误。

要具体查看是什么错误,需要调用 GetLastError 函数:

函数作用是返回上一个函数调用设置的线程32位错误代码。

这里截取了 <WinError.h>头文件里的部分错误代码:

可以看到,每个错误都有三种表示:

  1. MessageId: 一个消息ID,它是一个可以在源代码中使用的宏
  2. MessageText: 消息文本,它是描述错误的英文文本
  3. 编号: 应尽量避免使用它,我们可以看到,ERROR_SUCCESS 的编号是 0L

Windows核心编程说的39 000行还是少了,目前已经增加到了约 54 600 行…

 

当Windows函数调用失败后,应马上调用 GetLastError,因为加入又调用了另一个Windows函数,则此值可能改写。

注意:成功调用的Windows函数可能用 ERROR_SUCCESS 改写此值

 

一些Windows函数的调用成功,可能是缘于不同的原因。例如,创建一个命名的事件内核对象时,以下两种情况均会成功:

  1. 对象实际创建完成
  2. 存在一个同名的事件内核对象

Microsoft选择采用 “上一个错误代码”机制来返回这种信息,所以特定函数调用成功时,可以调用 GetLastError 函数来确定额外的信息。

例如对于创建事件内核对象,如果对象已经存在,则会返回 ERROR_ALREADY_EXISTS。

 

同时,我们也可以在VS中监视这个值,在Watch(监视)窗口中,输入 $err,hr 即可查看。

 

Windows提供了一个函数,可以将错误代码转换为相应的文本描述。此函数为:

该函数的使用会在后讲述。

 

1.1 定义自己的错误代码

在编程中,我们可能需要写自己的函数供他人调用,这个函数可能会因为某些原因失败,所以需要向调用者指出错误。为了指出错误,只需设置线程上的一个错误代码,然后令自己的函数返回FALSE、INVALID_HANDLE_VALUE、NULL或其他合适值。

通过下面的函数可以传递我们认为合适的任何32位的值:

建议尽量使用 <WinError.h> 中现有的代码——只要代码能很好地反映我想报告的错误。

如果其中任何一个代码都不能准确地反映一个错误,就可以创建自己的代码。错误代码是一个32位数。

错误代码的不同字段如下:

错误代码的不同字段

要注意的是第29位:

  • 如果创建自己的错误代码,就必须在此位放入一个1.
  • 如果是Microsoft生成的错误代码,则此位…承诺会一直为0.

注意第 27~16位:前256个值是为Microsoft保留的,其余的值可由我们应用程序来定义。

 

1.2 ErrorShow 示例程序——FormatMessage 函数的使用

展示处理部分的代码:

 

接下来详细讲述下 上文中 FormatMessage 函数的使用:

第一个参数有三个值,它们的含义分别是:

  1. FORMAT_MESSAGE_FROM_SYSTEM 希望获得一个系统定义的错误代码对应的字符串。
  2. FORMAT_MESSAGE_IGNORE_INSERTS 允许获得含有%d占位符的消息
  3. FORMAT_MESSAGE_ALLOCATE_BUFFER 要求该函数分配一块容纳错误文本描述的内存

要注意的是:

  1. 存放错误文本内存所对应的句柄在 hlocal 中返回
  2. %d占位符被Windows用来提供更多上下文相关信息,如果不传递这个表示,就必须在 Arguments 参数中提供这些占位符的值。但是这对于ErrorShow程序来说是不可能的,因为消息的内容事先是未知地。

 

第三个参数指出想要查找的错误代码。

第四个参数指出要用什么语言来显示文本。以下是参数的获得方法:

通过 LANG_NEUTRAL  和 SUBLANG_NEUTRAL  生成一个0值——操作系统默认语言。

第五个参数就是我们接受内存的句柄参数。

 

如果FormatMessage函数调用成功,文本描述就在上文的内存中,由 hlocal 句柄接受它。

如果调用失败,就在NetMsg.dll模块中查找消息代码,看错误是否和网络有关。

利用 NetMsg.dll 模块的句柄,再一次调用FormatMessage函数。

由此可见,每一个 DLL 或 .exe 都可以有自己的错误代码。

其实我们也可以创建自己的错误代码…MessageCompiler 来创建消息资源并添加到DLL或.ext模块中。

【Windows核心编程】错误处理
Tagged on:
0 0 投票数
Article Rating
订阅评论
提醒

0 评论
内联反馈
查看所有评论