《Windows Via C/C++》边学习,边翻译(一)错误处理(Error Handling)

《Windows Via C/C++》

Part I: Required Reading

Chapter List

Chapter 1: Error Handling
Chapter 2: Working with Characters and Strings
Chapter 3: Kernel Objects


第一章 错误处理(Error Handling)

Overview  概述

When you call a Windows function, it validates the parameters that you pass to it and then attempts to perform its duty. If you pass an invalid parameter or if for some other reason the action cannot be performed, the function's return value indicates that the function failed in some way.

当你调用一个Windows函数时,它会认证所传递参数的有效性并试图履行职责。如果你传递了无效参数或是由于其他原因而导致函数无法正常执行,函数返回值会指示出函数因某些原因而失败。

Table 1-1: Windows函数的通常返回值类型(Common Return Types for Windows Functions)

Date TypeValue to Indicate Failure
VOID

This function cannot possibly fail. Very few Windows functions have a return type of VOID.

此函数不可能失败。极少数Windows函数返回值为VOID。

BOOL

If the function fails, the return value is 0; otherwise, the return value is non-zero. Avoid testing the return value to see if it is TRUE: it is always best to test this return value to see if it is different from FALSE.

如果函数失败,返回值为零;否则,返回值为非零。避免检验此种返回值是否为TRUE,最好是检验它是否不为FALSE。

HANDLE

If the function fails, the return value is usually NULL; otherwise, the HANDLE identifies an object that you can manipulate. Be careful with this one because some functions return a handle value of INVALID_HANDLE_VALUE, which is defined as -1.
The Platform SDK documentation for the function will clearly state whether the function returns NULL or INVALID_HANDLE_VALUE to indicate failure.

如果函数失败,返回值通常是NULL,否则,HANDLE指向一个可操作的对象。对这类返回值要小心,因为其中一些函数返回INVALID_HANDLE_VALUE(定义为-1)的句柄值。
SDK文档中清楚地记述了此类函数返回NULL或INVALID_HANDLE_VALUE来指示失败。

PVOID

If the function fails, the return value is NULL; otherwise, PVOID identifies the memory address of a data block.

如果函数失败将返回NULL;否则返回PVOID指明数据块的内存地址。

LONG/DWORD

This is a tough one. Functions that return counts usually return a LONG or DWORD. If for some reason the function can't count the thing you want counted, the function usually returns 0 or -1 (depending on the function). If you are calling a function that returns a LONG/DWORD, please read the Platform SDK documentation carefully to ensure that you are properly checking for potential errors.

这是一种强硬的类型。函数返回计数通常返回LONG或DWORD类型。如果由于一些原因函数无法对想要计数的东西进行计数的话,函数通常返回0或-1(依赖于函数)。如果调用返回值是LONG/DWORD型的函数,请认真阅读SDK文档来确认你已正确检查了潜在的错误。

 

When a Windows function returns with an error code, it's frequently useful to understand why the function failed. Microsoft has compiled a list of all possible error codes and has assigned each error code a 32-bit number.

Windows函数返回的错误码,通常用来理解函数为什么会失败。Microsoft编制出所有可能错误码的列表,赋给每个错误码一个32位数。

Internally, when a Windows function detects an error, it uses a mechanism called thread-local storage to associate the appropriate error-code number with the calling thread. (Thread-local storage is discussed in Chapter 21, "Thread-Local Storage.") This mechanism allows threads to run independently of each other without affecting each other's error codes. When the function returns to you, its return value indicates that an error has occurred. To see exactly which error this is, call the GetLastError function: 

DWORD GetLastError();

This function simply returns the thread's 32-bit error code set by the last function call.

当Windows函数内部检测到一个错误时,它使用一种称为“线程本地存储”的机制,将适当的错误码与调用它的线程关联起来(线程本地存储将在第21章讨论)。这种机制允许线程彼此独立地运行,并且不会影响其它线程的错误码。当函数返回时,返回值指示发生了错误。通过调用GetLastError函数,能够精确得知是什么错误。此函数简单地返回线程最近一次函数调用(失败)所设置的32位错误码。

// 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   
  
//   
// MessageId: ERROR_PATH_NOT_FOUND   
//   
// MessageText:   
//   
// The system cannot find the path specified.   
//  
#define ERROR_PATH_NOT_FOUND             3L  

// 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

//
// MessageId: ERROR_PATH_NOT_FOUND
//
// MessageText:
//
// The system cannot find the path specified.
//
#define ERROR_PATH_NOT_FOUND             3L


As you can see, each error has three representations: a message ID (a macro that you can use in your source code to compare against the return value of GetLastError), message text (an English text description of the error), and a number (which you should avoid using and instead use the message ID). Keep in mind that I selected only a very tiny portion of the WinError.h header file to show you; the complete file is more than 39,000 lines long!

正如你所见,每种错误含有三种表述:一个message ID(代码中用来与GetLastError的返回值进行比较的宏名),一段message text(此错误的文本描述,英文),一个数值(应避免直接使用此数值,而应使用message ID)。请记住此处只选择了WinError.h头文件中的很小一部分;整个文件超过39,000行!

When a Windows function fails, you should call GetLastError right away because the value is very likely to be overwritten if you call another Windows function. Notice that a Windows function that succeeds might overwrite this value with ERROR_SUCCESS.

当Windows函数失败时,应立即调用GetLastError函数,因为当再调用其他Windows函数时,错误值可能已经被改写了。注意:当函数成功时,此值应该会改写成ERROR_SUCCESS。

Some Windows functions can succeed for several reasons. For example, attempting to create a named event kernel object can succeed either because you actually create the object or because an event kernel object with the same name already exists. Your application might need to know the reason for success. To return this information to you, Microsoft chose to use the last error-code mechanism. So when certain functions succeed, you can determine additional information by calling GetLastError. For functions with this behavior, the Platform SDK documentation clearly states that GetLastError can be used this way. See the documentation for the CreateEvent function for an example where ERROR_ALREADY_EXISTS is returned when a named event already exists.

一些函数会因为多种原因运行成功。例如,试图创建一个named event内核对象时,实际创建了对象、或是已经存在拥有相同名称的内核对象,两种情况下函数都会成功。应用程序需要知道成功的原因,微软通过使用last error-code机制返回此信息。因此当函数运行因某种原因成功时,可以通过调用GetLastError确定此附加信息。SDK文档中对于具有此类行为的函数,清晰地表述了其使用GetLastError的方式。例如,可以查看文档中对CreateEvent函数记述,当named event已存在时,调用GetLastError会返回ERROR_ALREADY_EXISTS值。

While debugging, I find it extremely useful to monitor the thread's last error code. In Microsoft Visual Studio, Microsoft's debugger supports a useful feature—you can configure the Watch window to always show you the thread's last error code number and the text description of the error. This is done by selecting a row in the Watch window and typing $err,hr.

在调试时监视线程的last error code非常有用。微软Visual Studio中的调试器提供这一有用的特性——你可以配置Watch视窗一直显示线程的last error code及其文本描述。做法是选择Watch视窗中的一行并键入$err,hr(@err,hr)。

Visual Studio also ships with a small utility called Error Lookup. You can use Error Lookup to convert an error code number into its textual description.

Visual Studio也提供了名为Error Lookup的小工具,可以将错误码转换成它对应的文本描述。

If I detect an error in an application I've written, I might want to show the text description to the user. Windows offers a function that converts an error code into its text description. This function is called FormatMessage:

当应用程序监测到了一个错误并想显示给用户的话,Windows提供了将错误码转换成对应的文本描述的函数。这个函数是FormatMessage:

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


FormatMessage is actually quite rich in functionality and is the preferred way of constructing strings that are to be shown to the user. One reason for this function's usefulness is that it works easily with multiple languages. This function takes a language identifier as a parameter and returns the appropriate text. Of course, first you must translate the strings yourself and embed the translated message table resource inside your .exe or DLL module, but then the function will select the correct one. The ErrorShow sample application (shown later in this chapter) demonstrates how to call this function to convert a Microsoft-defined error code number into its text description.

FormatMessage函数的功能强大,是向用户显示(信息)时首选的构造字符串的方式。此函数用途强大的一个原因是它支持多语言,将语言标识符作为一个参数以返回适当的文本。当然首先你必须自己将信息翻译好并嵌入到你的.exe或DLL模块的转换message table resource中。


Defining Your Own Error Codes  定义你自己的错误码

Microsoft also makes this mechanism available to you for use in your own functions. Let's say you're writing a function that you expect others to call. Your function might fail for one reason or another and you need to indicate that failure back to your caller.

微软为你编写的函数提供了此种机制。假设你希望别你调用了你所编写的函数,你的函数可能会由于这样或那样的原因而失败,你需要为调用者指示失败(原因)。

To indicate failure, simply set the thread's last error code and then have your function return FALSE, INVALID_HANDLE_VALUE, NULL, or whatever is appropriate. To set the thread's last error code, you simply call

VOID SetLastError(DWORD dwErrCode);

要指示失败,可以通过调用SetLastError函数简单地设置线程的last error code,并让函数返回FALSE、INVALID_HANDLE_VALUE、NULL,或其它适当的值。

passing into the function whatever 32-bit number you think is appropriate. I try to use codes that already exist in WinError.h—as long as the code maps well to the error I'm trying to report. If you don't think that any of the codes in WinError.h accurately reflect the error, you can create your own code. The error code is a 32-bit number that is divided into the fields shown in Table 1-2.

给函数传递一个适当的32位数值。尽量使用已在WinError.h中存在的值——只要它能反映所要报告的错误。如果没有能精确反映当前错误的既定值,你就应该创建自己的错误码。32位错误码的字段划分如表1-2所示:

Table 1-2: 错误码字段划分(Error Code Fields)

Bits:31-3029 28
Contents
内容
Severity
区分
Microsoft/customer
微软/用户
Reserved
保留
Meaning
意义
0=Success 成功
1 = Informational 信息
2 = Warning 警告
3 = Error 错误
0 = Microsoft-defined code
1 = customer-defined code
Must be 0

Bits:27-1615-0
Contents
内容
Facility code
设备码
Exception code
异常码
Meaning
意义
The first 256 values are reserved by Microsoft
前256个值为微软保留用
Microsoft/customer-defined code


These fields are discussed in detail in Chapter 24, "Exception Handlers and Software Exceptions." For now, the only important field you need to be aware of is in bit 29. Microsoft promises that all error codes it produces will have a 0 in this bit. If you create your own error codes, you must put a 1 in this bit. This way, you're guaranteed that your error code will never conflict with a Microsoft-defined error code that currently exists or is created in the future. Note that the Facility field is large enough to hold 4096 possible values. Of these, the first 256 values are reserved for Microsoft; the remaining values can be defined by your own application.

这些字段将在第24章“异常处理和软件异常”中讨论。现在你需要注意的仅仅是bit-29,微软约定自己定义的错误码的此位均为零,而用户定义的错误码应将此位置位。这样可以保证用户所定义的错误码,不会和微软目前已有的及将来所定义的错误码冲突。注意Facility field字段足够定义4096个数值,但是其中的前256个为微软保留用;剩余的值可以由用户自己定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值