《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 Type Value 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-30 29  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-16 15-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個爲微軟保留用;剩餘的值可以由用戶自己定義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章