TIEM:18:21 2011/10/24
chapter1:Error Handling
overview
Before we jump in and start examining the many features that Microsoft Windows has to offer,
you should understand how the various Windows functions perform their error handling.
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.
Table 1.1 shows the return value data types that most Windows functions use.
-------------------------------------------------
Table1.1:Common Return Types for Windows Functions.
Data Type Value to Indicate Failure
VOID This function can not possibly fail.Very few Windows functions have a return type of 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 alway best to test this return value to see
if it is different from FALSE.(HYP:And the reason ,and why that goes)
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 return NULL or INVALID_HANDLE_VALUE to indicate failure.
PVOID If the function fails,the return value is NULL;otherwise,PVOID identifies the memory address of a data block.
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.
----------------------------------------------------
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.
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.
Now that you have the 32-bit error code number,you need to translate that number into something more useful.
The WinError.h header file contains the list of Microsoft-defined error codes.
I'll reproduce some of it here so that you can see what it looks like:
//MessageId:ERROR_SUCCESS
//
//MessageText;
//
//The operaton complete sucessfully
//
#define ERROR_SUCCESS 0L
(HYP:More details to see,please see my other passage showing the whole of WinError.h.
Oh ,sorry to say that,i failed to paste the text of winerror.h to my blog passage.
so ,now i assign the file with this passage.)
……
……
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 messge 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 39000 lines long !
(hyp:amazing about it..in the previous editon of this book ,it says it's about more than 21000 lines long
,becasue it is win98 and win2000.now ,when comes to win nt,and win vista.it changes to so big number.
so ,i have to say that windows is really a large project...more and more powerful in the future and more and more complex in the future)
When a Windows function fails,you should call GetLastError right away because the value is very likely to be overwritten if you call anothr Windows function.
Notice that a Windows function that succeeds might overwrite this value with ERROR_SUCCESS.
(HYP:SO You must know how to use it actually)
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 dertermine addtitional 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.
(HYP:OK,to see the context of CreateEvent references ,please see my other passage)
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 windows 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.
Examine FIGURE 1.1:You'll see that i've called the CreateFile function.
This function returned a HANDLE of INVALID_HANDLE_VALUE(-1),indicating that it failed to open the specified file.
But the Watch window shows us that the last error code(the error code that would be returned by the GetLastError function if i called it)is 0x00000002.
Thanks to the ,hr qualifier ,the Watch window further indicates that error code 2 is "The system cannot find the file specified."
You'll notice that this is the same string mentioned in the WinError.h header file for error code number 2.
-----------------------------------------
Figure1.1:
Using $err,hr in Visual Studio's Watch window to view the current thread's last error code.
-----------------------------------------
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.
(hyp tips:Tools>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:
(hyp:suddenly,i think of qq and JinshanTpan,when internet can not be connected ,both its logon will fail,and shows one error code
with error text descriptin.so that are really useful skills.Do i need to convert winerror.h into chinese format error description.
may be that would be nice.Not all,but some necessary and important ones.Not directly ,but indirectly)
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.
Every now and then,someone asks me if Microsoft produces a master list indicating all the possible error codes that can be returned from every Windows function.
The answer ,unfortunately,is no.
What's more,Microsoft will never produce this list--it's just too difficult to construct and maintain as new versions of the system are created.
The problem with assembling such a list is that you can call one Windows functin,
but internally that funtion might call another function,and so on.
Any of these functions could fail,
for lots of different reasons.
Sometimes when a function fails,the higher-level function can recover and still perform what you want it to do.
To create this master list,Microsoft would have to trace the path of every function and build the list of all possible error codes.
This is difficult.
And as new versions of the system were created,these function-execution paths would change.
(YEAH,the author says the truth,maybe just because he is one man of the coders for windows developing.He knows much about it.
he is familiar enough with it,so experience make hime never think about this question.it just cannot be approachable( [ə'prəutʃəbl]) absolutely.)
############################################################3
Defining Your Own Error Codes
OK,i've shown you how Windows functions indicate errors to their callers.
Microsoft also makes this mechanism available to you for use in your own function.
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.
[
TAG001:
appropriate [ə'prəuprieit]
a. 适当的,合适的
v. 拨出,挪用,盗用
Five thousand dollars has been appropriated for the new school buildings.
已拨款五千元为建设新校舍之用
He appropriated public funds for his own private use.
他挪用公款
]
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 SetLastErro(DWORD dwErrCode);
passing into the function whatever 32-bit number you think is appropriate.
I try to use codes that already exists 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 actually 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.
---------------------------------------------------
Table1.2:Error Code Fields
Bits: 31-30 29 28 27-16 15-0
Contents Severity Microsoft/customer Reserved Facility code Exception Code
Meaning 0=Success 0=Microsoft-defined code Must be 0 The first 256 values are Microsoft/customer-defined code
1=Information 1=customer-defined code reserved by Microsoft
2=Warning
3=Error
------------------------------------------------------------
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.
(
hyp:
29-bit is a real utility...When designing it,The Microsoft engineers concern about future.
It distinct Windows and customer level error code perfectly.
So,if you design something ,think about future as much as you can...
that do much good to you...
)
(
hyp:(word design can both be a noun and a verb)
WOW,vey beautiful design ,ok,now ,just let me open the WinError.h please,and i get this which tells the truth matching words up.
//
// 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
//
)