Windows 错误处理机制

     

     在开发当中难免会出现函数调用失败,一般情况下我们肯定很急切的想知道是什么致使函数调用失败,作为初级程序员的我们一般为了判断自己的函数调用失败的原因都会选择使用很多的if-else函数来实现,但是要是函数当中会有很多执行失败那么if-else会使我们的函数很杂乱。为了解决这种问题Microsoft提供了错误处理机制。

   在一个函数调用完之后我们可以运用两个函数:

           GetLastError();

           SetLastError():

   首先GetLastError会返回我们上次函数的执行情况,它的返回值为一个32位的编号:根据此编号我们可以去WinError.h中查找;WinError.h 结构如下所示: 

/************************************************************************
*                                                                       *
*   winerror.h --  error code definitions for the Win32 API functions   *
*                                                                       *
*   Copyright (c) Microsoft Corp. All rights reserved.                  *
*                                                                       *
************************************************************************/

#ifndef _WINERROR_
#define _WINERROR_

#if defined (_MSC_VER) && (_MSC_VER >= 1020) && !defined(__midl)
#pragma once
#endif

#ifndef FORCEINLINE
#if (_MSC_VER >= 1200)
#define FORCEINLINE __forceinline
#else
#define FORCEINLINE __inline
#endif
#endif

#include <specstrings.h>

//
//  Note: There is a slightly modified layout for HRESULT values below,
//        after the heading "COM Error Codes".
//
//  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_XPS                                    82
#define FACILITY_WINRM                   51
#define FACILITY_WINDOWSUPDATE           36
#define FACILITY_WINDOWS_DEFENDER        80
#define FACILITY_WINDOWS_CE              24
#define FACILITY_WINDOWS                 8
#define FACILITY_USERMODE_VOLMGR         56
#define FACILITY_USERMODE_VIRTUALIZATION 55
#define FACILITY_USERMODE_VHD            58
#define FACILITY_URT                     19
#define FACILITY_UMI                     22
#define FACILITY_UI                      42
#define FACILITY_TPM_SOFTWARE            41
#define FACILITY_TPM_SERVICES            40
#define FACILITY_SXS                     23
#define FACILITY_STORAGE                 3
#define FACILITY_STATE_MANAGEMENT        34
#define FACILITY_SSPI                    9
#define FACILITY_SCARD                   16
#define FACILITY_SHELL                   39
#define FACILITY_SETUPAPI                15
#define FACILITY_SECURITY                9
#define FACILITY_SDIAG                   60
#define FACILITY_RPC                     1
#define FACILITY_RAS                     83
#define FACILITY_PLA                     48
#define FACILITY_OPC                     81
#define FACILITY_WIN32                   7
#define FACILITY_CONTROL                 10
#define FACILITY_WEBSERVICES             61
#define FACILITY_NULL                    0
#define FACILITY_NDIS                    52
#define FACILITY_METADIRECTORY           35
#define FACILITY_MSMQ                    14
#define FACILITY_MEDIASERVER             13
#define FACILITY_MBN                     84
#define FACILITY_INTERNET                12
#define FACILITY_ITF                     4
#define FACILITY_USERMODE_HYPERVISOR     53
#define FACILITY_HTTP                    25
#define FACILITY_GRAPHICS                38
#define FACILITY_FWP                     50
#define FACILITY_FVE                     49
#define FACILITY_USERMODE_FILTER_MANAGER 31
#define FACILITY_DPLAY                   21
#define FACILITY_DISPATCH                2
#define FACILITY_DIRECTORYSERVICE        37
#define FACILITY_CONFIGURATION           33
#define FACILITY_COMPLUS                 17
#define FACILITY_USERMODE_COMMONLOG      26
#define FACILITY_CMI                     54
#define FACILITY_CERT                    11
#define FACILITY_BCD                     57
#define FACILITY_BACKGROUNDCOPY          32
#define FACILITY_ACS                     20
#define FACILITY_AAF                     18
// Define the severity codes
// MessageId: ERROR_SUCCESS
//
// MessageText:
//
// The operation completed successfully.
//
#define ERROR_SUCCESS                    0L

#define NO_ERROR 0L                                                 // dderror
#define SEC_E_OK                         ((HRESULT)0x00000000L)

//
// MessageId: ERROR_INVALID_FUNCTION
//
// MessageText:
//
// Incorrect function.
//
#define ERROR_INVALID_FUNCTION           1L    // dderror

//
// MessageId: ERROR_FILE_NOT_FOUND
//
// MessageText:
//
// The system cannot find the file specified.
//
#define ERROR_FILE_NOT_FOUND             2L

     可以看出,每个错误都有三种表示:一个消息ID(一个可在源代码中使用的宏,用于与GetLastError的返回值进行比较)、消息文本(描述错误的英文文本)和一个编号(应该避免使用此编号,尽量使用消息ID)。注意,我只摘录了WinError.h头文件的极小一部分,整个文件的长度超过39 000行!

一个Windows函数失败之后,应该马上调用GetLastError,因为假如又调用了另一个Windows函数,则此值很可能被改写。注意,成功调用的Windows函数可能用ERROR_SUCCESS改写此值。

一些Windows函数调用成功可能是缘于不同的原因。例如,创建一个命名的事件内核对象时,以下两种情况均会成功:对象实际地完成创建,或者存在一个同名的事件内核对象。应用程序也许需要知道成功的原因。为返回这种信息,Microsoft选择采用・上一个错误代码(last error code)机制。所以,当特定函数成功时,你可以调用GetLastError来确定额外的信息。对于具有这种行为的函数,Platform SDK文档会清楚指明能以这种方式使用GetLastError。文档中提供了CreateEvent函数的一个例子;如果存在命名的事件,它会返回ERROR_ALREADY_EXISTS。

     对于查找错误信息的文本格式我们特别要提到的就是:FormatMessage()函数。

   DWORD FormatMessage(

   DWORD dwFlags,

   LPCVOID pSource,

   DWORD dwMessageId,

   DWORD dwLanguageId,

   PTSTR pszBuffer,

   DWORD nSize,

   va_list *Arguments) 
     FormatMessage的功能实际相当丰富,为了构造要向用户显示的字符串,它是首选的一种方式。之所以说它好用,一个原因是它能轻松地支持多种语言(译注:这里的语言是自然语言,比如汉语、英语等等,而不是计算机编程语言)。它能获取一个语言标识符作为参数,并返回那种语言的文本。当然,你首先必须翻译好字符串,并将翻译好的消息表(message table)资源嵌入自己的.exe或DLL模块中。但在此之后,这个函数就能自动选择正确的字符串。ErrorShow示例程序(参见后文)演示了如何调用这个函数将Microsoft定义的错误代码编号转换为相应的文本描述。

定义自己的错误代码前面讲述了Windows函数如何向其调用者指出错误。除此之外,Microsoft还允许将这种机制用于你自己的函数中。假定你要写一个供其他人调用的函数。这个函数可能会因为这样或那样的原因而失败,所以需要向调用者指出错误。为了指出错误,只需设置线程的上一个错误代码,然后令自己的函数返回FALSE,INVALID_HANDLE_VALUE、NULL或者其他合适的值。为了设置线程的上一个错误代码,只需调用以下函数,并传递你认为合适的任何32位值:

VOID SetLastError(DWORD dwErrCode);

我会尽量使用WinError.h中现有的代码??只要代码能很好地反映我想报告的错误。如果WinError.h中的任何一个代码都不能准确反映一个错误,就可以创建自己的代码。错误代码是一个32位数,由表1-2描述的几个不同的字段组成。

表1-2错误代码的不同字段位:

1-2错误代码的不同字段位:

31–30

29

28

27–16

15–0

内容

严重性

Microsoft/

客户

保留

Facility

代码

异常代码

含义

0 =

成功

1 =

信息(提示)

2 =

警告

3 =

错误

0 = Microsoft

定义的代码

1 =

客户定义的代码

必须为

0

256个值由Microsoft保留

Microsoft/

客户定义的代码

    

               / / 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("netmsg.dll"), NULL,

     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("No text found for this error number."));  

       第一行从编辑控件获取错误代码。然后,指向一个内存块的句柄被实例化并初始化为NULLFormatMessage函数在内部分配内存块,并返回指向这个内存块的句柄。调用FormatMessage时,我们向它传入了FORMAT_MESSAGE_FROM_SYSTEM标志。该标志告诉FormatMessage:我们希望获得与一个系统定义的错误代码对应的字符串。另外,还传入了FORMAT_MESSAGE_ALLOCATE_BUFFER标志,要求该函数分配一个足以容纳错误文本描述的内存块。此内存块的句柄将在hlocal变量中返回。FORMAT_MESSAGE_IGNORE_INSERTS标志则允许你获得含有%占位符的消息。

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值