1. Scale of Microsoft to anomalous processing method
Time, I summarized the processing method of unusual classification and C standard library support. This time discussed Microsoft's extension of these methods: structural exception handling (SEH) and Microsoft Foundation Class (MFC) abnormal processing. SEH is valid for C and C , and the MFC abnormal system is only valid for C .
1.1 Institutional abnormal treatment
The institutional abnormality processing is the service function provided by Windows and is valid for all the programs written in all languages. In Visual C , Microsoft encapsulates and simplifies these services (via non-standard keywords and library programs). Other compilers of the Windows platform may select different ways to reach similar results. In this column, the noun "structured exception handling" and "SEH" specializes in Visual C to the package of Windows exception service.
1.2 keyword
To support SEH, MICORSOFT extends C and C languages with four new keywords:
l __except
l __finally
l __leave
l __try
Because this is a non-standard keyword, it must be opened after the extension option must be opened (turned off / fa).
Why are these keywords underline? C standard (Terms 17.3.1.2, "Global Names"):
The following names and functions are always reserved to the compiler:
l All with double underscores (__) or with a name of a lower-line plus a big write letter reserved to the compiler.
l All names starting with a underscore are reserved to the compiler as the global name.
The C standard has a similar state of affirmation.
Since the keyword of SEH meets the above rules, Microsoft has the right to use them. This also shows that you are not allowed to use the reserved name in your own program. You must avoid defining the names like __myheader_h__ or _fatalerror identifier.
Interestingly and unfortunately, the source code generated by Visual C Application Wizards uses the reserved identifier. For example, if you generate a new service with ATL COM App Wizard, the result frame code defines the name of the _handler and _twinmain - the reserved name that you cannot use by the standard.
To reduce this non-specified behavior, you can of course manually change these names. Fortunately, these questions are all private variables, which are not visible outside the definition of the class, and global replacement is possible in .H and .CPP. Unfortunately, there is a function (_twinmain) and an object (_Module) has been declared extern, which means that other parts of the program will assume that you have used these names. (In fact, Visual C library libc.lib requires names when connecting_twinmain available.)
I suggest you keep the name of Wizard, don't define such a name in your own code. In addition, you should write all non-standard definitions to documents and leave for maintenance personnel; remember that Visual C (and existing other C compilers) may use these names in another way, thereby destroying Your code.
1.3 identifier
Microsoft also defines several SEH identifiers in the non-header file ExcPt.h, and includes in Windows.h. Inside it, define: Lilting Expression of the L-to-En-EXCEPT using the filtering result macro.
l Win32 objects and functions of the alias macro are used to query the exception information and status.
l The false keyword macro, and the four keywords mentioned earlier have the same name and meaning, but there is no underscore. (For example, macro Leave corresponds to SEH keyword __leave.)
Microsoft uses these macro let me crazy. They define multiple individual names for the same function. For example, Excpt.h has the following statements and definitions:
Unsigned long __cdecl _exception_code (void);
#define getExceptioncode_exception_code
#define exception_code _exception_code
That is, you can call the same function in three ways. Which one do you use? And do you have maintained as you expect?
In Microsoft's documentation, it looks preference GetExceptioncode, its name and other global Windows API functionality. I searched for 33 getExceptioncode in MSDN, two _exception_code, and the exception_code number is zero. According to Microsoft's boot, it is recommended to use the getExceptioncode and other functions of similar names.
Because _Exception_code's two alias is a macro, you can't use the same name anymore. I have made this mistake when I wrote the routine for this column. I defined a partial object called Exception_Code (probably). In fact, I just define a partial object called _exception_code, which is the result of the macro Exception_code that I am inadvertently used. When I think of this problem, the solution is simply change my object name from Exception_Code to Code.
Finally, ExcPt.h defines a special macro - "try" - has become a C real keyword. This means you can't simply mix the SEH and standard C unusual blocks in the compilation unit containing Excpt.h, unless you are willing to #undef this TRY macro. When such undef reveals the real TRY keyword, it is necessary to take the risk of the SEH's maintenance personnel. On the other hand, the programmer mastering standard C will understand TRY as a keyword rather than a macro.
I think that includes a header file (even if an unmptist file like Excpt.h) should not change the behavior of the language standard. I have more insisted that the keyword defined by covering or redefining the definition of the language standard is a bad habit. I suggest: #undef try, the same use of other pseudo-key macros, directly use the real keyword (such as __TRY).
1.4 grammar
The most basic SEH syntax is the TRY block. The following form:
__TRY Compound-Statement Handler
Processing body:
__except (filter-expression) Compound-Statement
or:
__finally company-statement
Look at it, the TRY block is as follows:
__TRY
{
...
}
__except (filter-expression)
{
...
}
or:
__Try {
...
}
__finally
{
...
}
In __Try you must use a Leave statement:
__TRY
{
...
__leave;
...
}
In a larger block, a TRY block is considered a single statement:
IF (x)
{
__TRY
{
...
}
__finally
{
...
}
}
Equivalent to:
IF (x)
__TRY
{
...
}
__finally
{
...
}
Other attention:
l You must have a correct exception handler in a given TRY block.
l All statements must be merged. Even if there is only one statement, it must put it in {} after __Try, __ except, or __finally.
l In an exception handler, the corresponding filtering expression must have one or can be converted to an INT type value.
1.5 Basic language
The last time I list 5 stages of the abnormal life. In the SEH system, these stages are achieved as follows:
l The operating system has reported a hardware error or detects a software error, or the user code detects an error (stage 1).
L (usually call the Win32 function rasieeexception start, the operating system generates and triggered an exception object (stage 2). This object is a structure whose property is visible to the exception handler.
l The exception handler "See" exception, and has the opportunity to capture it (stages 3 and 4). It is an exception or recovery or termination depending on the will of the processing function. (Stage 5).
A simple example:
Int filter (void)
{
/ * Stage 4 * /
}
Int main (void)
{
__TRY
{
IF (Some_ERROR) / * Stage 1 * /
RaiseException (...); / * stage 2 * /
/ * Stage 5 of results Exception * /
}
__except (filter ()) / * stage 3 * /
{
/ * Stage 5 of Terminating Exception * /
}
Return 0;
}
Microsoft call defines the exception handler in __except, and defines the termination function in __finally.
Once an exception is triggered, the exception handler starting from __except is an exception occurs inquiry. Each discovered exception handler, its filtration expression is evaluated. What happens after each request depends on its return results.
Excpt.h defines the macros of 3 filter results, all of which:
l EXCEPTION_CONTINUE_EXECUTION = -1
l Exception_Continue_Search = 0
l EXCEPTION_EXECUTE_HANDLER = 1
As I said before, the filter expression must be compatible with the INT type, so they match the value of these three macros. This statement is too conservative: My experience shows that the filter expressions accepted by Visual C can have all intensive, pointer, structures, arrays even VOID types! (But I have encountered compilation errors when trying floating point pointers.)
Further, all the values obtained are valid (at least for integer). All non-zero and the value of the symbol bit is equivalent to the exception_execute_handler, and the symbol bit is equivalent to exception_continue_execution. This is probably the result of bitmaping. If an exception handler is filtered, the result is Exception_Continue_Search, which refuses to capture an exception and will continue to search for the next exception handler.
By generating a non-Exception_Continue_Search by a filter expression to capture an exception, once captured, the program is restored. How to recover the value of the filter expression:
l Exception_Continue_execution: The manifestation is recovered. Be started under the abnormality occurring. The code that the exception handler itself is not executed.
l EXCEPTION_EXECUTE_HANDLER: The performance is termination exception. Starting from the abnormality occurrence, the termination function encountered along the way is executed. The stack returns to the level of the processing function of the captured exception. Enter the process of handling the function and execute.
As shown in the name, the termination processing function (the code started with __finally) is called when terminating anomalies. The inside is the Clean UP code, which is like the ATExit () function and C destructor in the C standard library. Termination processing functions will also enter the normal execution process, just like not capture code. Instead, the abnormal processing function is always induced by a capture: they only enter when it evaluates Exception_execute_handler at its filter expression.
Termination processing functions do not expressly know that they are from normal processes or they enter when a try block is extremely terminated. To determine this, you can call the AbnormalterMination function. This function returns an int, 0 indicates that from the normal process, and other values indicate that they enter when the exception is terminated.
Abnormaltermination is actually a macro pointing_abnormal_termination (). Visual C will design _abnormal_termination () as an environmentally sensitive function, just like a keyword. You can't call this function, you can only call in the termination processing function. This means that you can't call a middle function in the termination handler, and then call _abnormal_termination () in this intermediate function, so you will get a compile period error.
1.6 routines
The following C example shows the different filter expressions and the interaction of the processing function itself. The first version is a small full program, and the subsequent version has a small change in front of it. All versions are inscribed, you can see processes and behaviors.
The program triggers an exception object via raiseexception (). The first parameter of the raiseException () function is an exception code. The type is 32-bit unsigned integer (DWORD); Microsoft has kept the user-defined error to retain the range of [0xE0000000, 0xEfffffff]. Other parameters are typically filled.
The abnormal filter used here is simple. In actual use, you have to call getExceptioncode () and getExceptioninformation () to query the properties of the exception object.
1.7 Version # 1: Terminating Exception
Generate an empty Win32 console program with Visual C , named SEH_TEST, and the option is default. Add the following C source code to the project file:
#include
#include "windows.h"
#define filter (Level, Status) /
(/
Printf ("% s:% * sfilter =>% s / n", / # level, (int) (2 * (level), "", #status), /
(status) /
)
#define Termination_Trace (Level) /
Printf ("% s:% * shandling% Snormal Termination / N", /
#level, (int) (2 * (level), "" ", /
ABNORMALTERMINATION ()? "Ab": "")
Static void trace (int level, char const * message)
{
Printf ("% D:% * S% S / N", Level, 2 * Level, "", Message);
}
Extern Int Main (Void)
{
DWORD const code = 0xE0000001;
Trace (0, "Before First Try");
__TRY
{
Trace (1, "TRY");
__TRY
{
Trace (2, "TRY");
__TRY
{
Trace (3, "TRY");
__TRY
{
Trace (4, "TRY");
Trace (4, "Raising Exception");
RaiseException (CODE, 0, 0, 0);
Trace (4, "inster exception");
}
__finally
{
Termination_Trace (4);
}
END_4:
Trace (3, "Continuation");
}
__except (Filter (3, Exception_Continue_Search))
{
Trace (3, "HANDLING Exception");
}
Trace (2, "Continuation");
}
__finally
{
Termination_Trace (2);
}
Trace (1, "continalion");
}
__except (Filter (1, Exception_execute_Handler)
{
Trace (1, "HANDLING Exception");
}
Trace (0, "Continuation");
Return 0;
}
Compile code now. (You may get the unused warning of Label End_4; first ignore.)
note:
The L program has four nested TRY blocks, two have an exception handler, and two termination processing functions. In order to better display the nested and control flow, I put all them in the same function. Active programming may be placed in multiple functions or multiple compilation units.
l Tracking operation, output results show the nested level of the current block.
l The abnormal filter is implemented as a macro. The first parameter is the nested level, and the second is the value that is actually handled.
l Termination Processing Functions Tracks its implementation through the Termination_Trace macro, showing the causes of them. (Remember, the termination process will enter even if there is no exception.)
Run this program, you will see the following output:
0: Before first try
1: TRY
2: TRY
3: TRY
4: TRY
4: raising exception3: filter => Exception_Continue_Search
1: filter => EXCEPTION_EXECUTE_HANDLER
4: Handling Abnormal Termination2: Handling Abnormal Termination
1: HANDLING EXCEPTION
0: Continuation
Event chain:
l The fourth layer of TRY triggers an exception. This leads to the next search to search, and look for an exception filter that is willing to capture this exception.
l The first exception filter (in the third layer) derived Exception_Continue_Search, so refused to capture this exception. Continue to search for the next exception handler.
l The next abnormal filter (in the first layer) is obtained, exception_execute_handler. This time, this filter captures this exception. Because it is obtained, the exception will be terminated.
l The control returns to the abnormality, starting to get the stack. All termination processing functions along the road are run, and all the processing functions know that the exception is terminated. Stacked to control back to the exception handler (at the first layer). At the time of the stack, only other codes of the intermediates are ignored by the termination processing function being executed.
l Control Right An exception handler (in the first layer), will continue to execute in normal state.
Note that the control is transmitted twice in the same nested layer: the first exception filter expression evaluation, the second time when the stack and execution termination processing function are passed. This creates a hazard possible: what is modified if an abnormal filter expression is not desired in a certain termination handler. A basic principle is that your abnormal filter does not have side effects; if so, you must save them for your termination process.
1.8 version 2: unfuckled abnormal
Use this line in the routine:
__except (Filter (1, Exception_execute_Handler)
Change to
__except (Filter (1, Exception_Continue_Search))
There is no abnormal filter to capture this exception. Execute the modified program, you will see:
0: Before first try
1: TRY
2: TRY
3: TRY
4: TRY
4: Raising Exception
3: Filter => exception_continue_search
1: Filter => EXCEPTION_CONTINUE_SEARCH
Then there will be this dialog box:
1. User Unusual Dialog
Click "Details" to expand
2. Details of the user exception dialog
Seeing in the error message: The error program is SEH_TEST, and the original exception code thrown through RaiseException is E0000001H.
This exception leaked the program and was finally captured and processed by the operating system. Some like your program is written:
__TRY
{
Int main (void)
{
...
}
}
__except (Exception_Dialog (), Exception_execute_Handler)
{
}
According to "Close" on the dialog, all termination processing functions are executed and returned until the control returns to the processing function of the captured exception. You can see this information clearly:
4: Handling Abnormal Termination2: Handling Abnormal Termination
They appear after closing the dialog. Note that you didn't see:
0: Continuation
Because its implementation code is outside the termination of the process, only the termination processing function is executed when the stack is returned.
For our test procedures, capture an abnormal process function is outside of Main, which means that the behavior of passing an abnormal behavior is still going on outside the program. As a result, the program is terminated.