Windows系统错误处理机制

一、什么是错误

意为意为不正确,与正确答案相反。我们这里讲的是Windows操作系统里进程运行时产生的错误。对我们程序员来说,其实也就是我们编程过程中,调用Windows系统提供的API、COM 接口、内核驱动开发接口,这些API或接口失败时产生的错误。这些错误会导致我们的程序代码完成不了预设的功能或效果。

二、Windows 错误机制

首先,Windows会为各种API、接口失败的各种错误预先定义好,一种错误用一个错误码(Error Code)来表示。当错误产生时,API或接口会失败,有的是用返回值返回错误码,同时也表示调用成功或失败,比如COM接口;有的是用返回值代表成功或失败,错误码放在特定的区域,通过特定API去获取,比如大部分Win32 API,它将错误码存储在线程局部存储区里,永远记住最后一个错误码 ;有的是用状态码作为错误码,比如内核驱动开发接口

Windows系统的错误机制,当进程产生非致命错误时,它不会改变线程的执行路径,但这不代表对程序没有影响。如果一个错误发生,我们不处理,而让程序继续往下跑,可能会导致后面更严重的问题。那么我们在Windows上编程时,应该调用API和接口后,及时检测是否失败,如果失败,检测错误,看是什么错误,做不同处理。

在我们编程的过程种,经常遇到下面三种错误:

  • Win32 Error
  • COM Error
  • NTSTATUS

三、Windows 错误代码(Error Code)

3.1、Windows错误代码

由前面我们知道常见的三种错误,他们都是通过错误代码来代表不同错误消息,它是一个DWORD值,也就是32bit,那么这个值得每一位或几位代表什么意思,Windows是有规定的,错误代码域也就是说错误代码的定义规则是怎样的。

3.2、错误代码的生成
3.2.1、编写错误信息文本文件
应用程序可以根据业务需求按照Windows错误代码域的规则定义自己的错误码, 自定义错误码需要先编写一个错误信息文本文件。文件以.mc为后缀,其语法相对来说比较简单,该文件主要包括三部分,注释、信息头(Header Section)和信息体(Message Section)。
  • 注释
    注释是以分号(;)开头的行,在编译后生成的C/C++头文件中,MC编译器会去掉这些分号,也就是说,如果你要生成一些带C/C++的注释,在分号后再加入C/C++的注释即可,如:
;#ifndef _YOUR_MESSAGE_ERROR_TEXT_
;#define _YOUR_MESSAGE_ERROR_TEXT_

;// C/C++单行注释
;
;/*
; 
; C/C++块注释
; 
;*/
;#endif
  • 信息头部(Header Section)
    信息头部定义信息体中需要使用的一些名称和语言标识,可以包含以下0个或多个语法声明。
语法说明
MessageIdTypedef=type 声明错误码类型,该定义一般放到信息头部最前面,type在消息头文件(.h)中被使用。定义的类型(32位)必须要能够容纳所有的错误码。
SeverityNames=(name=number[:SymbolName]) 声明错误等级集合,在30-31位中定义。可以定义多个错误等级,以空格分隔。默认定义为:
SeverityNames=(Success=0x0 Informational=0x1 Warning=0x2 Error=0x3) ;name是错误等级名称,是在信息体中引用的名字。SymbolName是自定义的符号名,该符号编译后会在头文件中定义,具体见实例。
FacilityNames=(name=number[:SymbolName]) 声明设备代码集合,在16-27位中定义,可以定义多个设备代码,中间以空格分隔。如果29为没有标记为1,则前256位系统保留使用,应用可以在0x100-0xFFF中定义。name是设备代码名称,是在信息体中引用的名字。SymbolName是自定义的符号名,该符号编译后会在头文件中定义,具体见实例。
LanguageNames=(name=number:filename) 声明语言集合,其中name语句名称,是在信息体中需要引用的名字,可以支持多种语言,中间以空格分隔。number是语言标识,filename是包含对应语言的文件名,不带后缀,由MC编译器生成一个指定文件名加.bin的文件,用于存储对应语言的错误文本信息。语言标识(language identifier)由一个16bit的数组成,其中高6位是次语言标识(SubLanguage ID), 低10位是主语言标识(Primary Language ID)。关于语言标识的常量定义在这里。比如我要定义简体中文,主语言标识为0x04,次语言标识为0x02,故简体中文的语言标识为0x804,于是可以这样定义:
LanguageNames=(Chinese=0x804:MSG00804)
  • 错误信息体(Message Section)
    信息体在信息头之后定义,下表是一个信息体可能包含的一些定义,每个信息体以MessageId开头,以单独成行的句点结束。Severity和Facility可选。
语法说明
MessageId=[number|+number] 消息标识,必须要定义,但其值是可选的,如果没有定义值,将使用上一个消息id加1。如果使用了+号,消息标识为在上一个消息id上加指定的number。所定义的消息标识大小不能超过16位;
Severity=name 错误等级,如果Severity=Error;
Facility=name 设备代码,如Facility=Application;
SymbolicName=name 符号名称,该名称会被定义在C/C++头文件中,格式如:
#define name ((type)0xnnnnnnnn);其中type在信息头MessageIdTypedef中声明;
Language=name 信息语言,语言名称在信息头部定义,如果没定义默认为English。 如:Language=Chinese
message text 信息对应语言的文本;
. 以一个独立成行的句点作为信息体结束符;

如果一个信息体支持多种语言,对每一种语言都需要一个语言声明、语言文本和独立成行的句点,如:

MessageId=0x1
Severity=Error
Facility=Runtime
SymbolicName=MSG_BAD_COMMAND
Language=English
You have chosen an incorrect command.
.
Language=Chinese
<Chinese message string goes here>
3.2.2、编译(MC.exe)
编写好错误信息文本文件之后需要用Message Compiler(MC.exe)编译工具进行编译,mc的具体用法见这里。由于我们需要支持中文,所以需要指定-u参数,另外,如果我们想为设置29位为1,需要指定-c参数,如:

mc.exe -cu MessageTable.mc

编译成功后会生成四个文件:MessageTable.h、MessageTable.rc、MSG00409.bin、MSG00804.bin
其中MessageTable.h的内容如下:

#ifndef _ERROR_MESSAGE_TEXT_
#define _ERROR_MESSAGE_TEXT_

//
//  Values are 32 bit values laid out as follows:
//
//   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            |
//  +---+-+-+-----------------------+-------------------------------+
//
//  where
//
//      Sev - is the severity code
//
//          00 - Success
//          01 - Informational
//          10 - Warning
//          11 - Error
//
//      C - is the Customer code flag
//
//      R - is a reserved bit
//
//      Facility - is the facility code
//
//      Code - is the facility's status code
//
//
// Define the facility codes
//
#define FACILITY_TEST_3                  0x101
#define FACILITY_TEST_2                  0x101
#define FACILITY_TEST_1                  0x100


//
// Define the severity codes
//
#define ITEST_SEVERITY_WARNING           0x2
#define TEST_SEVERITY_SUCESS             0x0
#define ITEST_SEVERITY_INFORMATIONAL     0x1
#define TEST_SEVERITY_ERROR              0x3


//
// MessageId: TEST_E_01
//
// MessageText:
//
// Define english string error message for TEST_E_01
//
#define TEST_E_01                        ((HRESULT)0xE1000001L)

//
// MessageId: TEST_E_02
//
// MessageText:
//
// Define english string error message for TEST_E_02
//
#define TEST_E_02                        ((HRESULT)0xE1000002L)

//
// MessageId: TEST_E_03
//
// MessageText:
//
// Define english string error message for TEST_E_03
//
#define TEST_E_03                        ((HRESULT)0xE1000003L)

#endif

注意,在生成的头文件中发现没有错误信息的中文文本说明,由于生成的头文件中只展示第一个定义的语言文本说明,如果想展示中文说明的话,在信息体把中文定义放在第一位就行了。如:

MessageId=0x1 Severity=Error Facility=TestFacility1 SymbolicName=TEST_E_01
Language=Chinese
在这儿定义TEST_E_01的中文错误文本
.
Language=English
Define english string error message for TEST_E_01

更多参考:
https://www.jianshu.com/p/04a4ace9a31d

posted on 2019-07-08 15:03  活着的虫子  阅读(1281)  评论(0编辑  收藏  举报

导航