一、什么是错误

意为意为不正确,与正确答案相反。我们这里讲的是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

Windows系统错误处理机制的更多相关文章

  1. windows程序消息机制(Winform界面更新有关)

    windows程序消息机制(Winform界面更新有关) 转自:http://www.cnblogs.com/blosaa/archive/2013/05/31/3109586.html 1. Win ...

  2. windows程序消息机制(Winform界面更新有关)--转

    1. Windows程序消息机制 Windows GUI程序是基于消息机制的,有个主线程维护着消息泵.这个消息泵让windows程序生生不息. Windows程序有个消息队列,窗体上的所有消息是这个队 ...

  3. 深入详解windows安全认证机制ntlm&Kerberos

    0x01 为什么要理解windows 安全认证机制: 加深对后续各种漏洞利用的理解深度,还是那句话,要知其然,更要知其所以然,不废话,咱们直接开始 0x02 windows认证协议主要有以下两种: 基 ...

  4. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  5. 全面介绍Windows内存管理机制及C++内存分配实例

    转自:http://blog.csdn.net/yeming81/article/details/2046193 本文基本上是windows via c/c++上的内容,笔记做得不错.. 本文背景: ...

  6. windows 系统错误码总结

    windows 错误码大全: 操作成功完成. 功能错误. 系统找不到指定的文件. 系统找不到指定的路径. 系统无法打开文件. 拒绝访问. 句柄无效. 存储控制块被损坏. 存储空间不足,无法处理此命令. ...

  7. 逆向工程学习第四天--Windows栈溢出保护机制(GS)原理及绕过测试

    GS简介: Windows的缓冲区安全监测机制(GS)可以有效的阻止经典的BOF攻击,因为GS会在函数调用前往函数栈帧内压入一个随机数(canary),然后等函数返回前,会对canary进行核查,判断 ...

  8. 【笨嘴拙舌WINDOWS】消息机制

    如果将WINDOWS比做一个人,那么他就是为你提供各种服务的巫师,他手上有各式各样,奇形怪状的奇葩物品.他脑子充满了智慧,能够为你解决你所不能解决的疑难杂症.但是他不认识你! 你从小立志要想考状元,去 ...

  9. Windows程序消息机制浅析

    1.消息  消息是由MSG结构体来表示的.如下: typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lPar ...

随机推荐

  1. Verilog的各种坑

    Verilog语言和软件语言不一样,有些时候理所当然的编写,也没有报语法错误,可是功能就是不对.唉,把遇到的坑都记在本篇博客吧. 1. initial begin...end里面不能有always,如 ...

  2. 函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!闭包访问局部变量

    函数内部声明变量的时候,一定要使用var命令.如果不用的话,你实际上声明了一个全局变量! function f1(){ n=999; } f1(); alert(n); 子函数可以一层一层读取到父元素 ...

  3. 解决pyspider框架web预览框过小问题

    解决pyspider框架web预览框过小问题 Chrome 使用pyspider框架时,web预览框只有一小条: 解决办法: 找到debug.min.css文件,替换为如下内容: body{margi ...

  4. ActiveMQ(一)

    下载地址:http://activemq.apache.org/download.html 换数据源: ActiveMQ的独占消费

  5. c# Path.Combine

    Path.Combine: c#获取当前项目路径 : //获取包含当前执行的代码的程序集的加载文件的完整路径 var appPath = System.IO.Path.GetDirectoryName ...

  6. .net core使用ocelot---第六篇 负载均衡

    简介 .net core使用ocelot---第一篇 简单使用 .net core使用ocelot---第二篇 身份验证 .net core使用ocelot---第三篇 日志记录  .net core ...

  7. Part_four:redis主从复制

    redis主从复制 1.redis主从同步 Redis集群中的数据库复制是通过主从同步来实现的 主节点(Master)把数据分发从节点(slave) 主从同步的好处在于高可用,Redis节点有冗余设计 ...

  8. UCOSIII时间片轮转调度

    OS_RATE_HZ const OSCfg_TickRate_Hz = (OS_RATE_HZ )OS_CFG_TICK_RATE_HZ; #define OS_CFG_TICK_RATE_HZ 2 ...

  9. iOS ANE植入流程

    来源:http://www.adsmogo.com/help/iosANE 一.iOS ANE植入流程 Step 1:创建Flex工程 1.1 启动Flash Builder 4.6.0, 选择“Fi ...

  10. 我与SAP成都研究院吴院长的二三事

    这几天Jerry没怎么看手机,今天才注意到,昨天SAP中国研究院公众号上发布了一篇文章:SAP高管说: 体验经济时代下的SAP客户体验.仔细一看,这不是咱SAP成都研究院的吴院长么. 在今年没有发生部 ...