C Exception Handler2001-12-11
The basic idea of exception handling is the simplified error code that provides a standard detection mechanism for program key.
Maybe we have used an exception, but will you be a habit, don't always think when I open a file, I use an unusual judgment, I know that you like Return Value or Print Error Message. To do it, you think so will lead to Memory Leak, system exit, code repeat / hard reading, garbage a bunch ...? The current software is already N * 365 * 24 hours of operation, and the strength of the software is already a very consideration. Self-sequence: The writing program is really important, a robust code is not solved by returning ERROR Message / Return Value, but often from C, it is used to such a way. Only in this document will give me the meteor rain today. Fortunately, I can write this today, otherwise it will be the fourth pass; at the same time, I would like to thank Jeffrey Masters. This article can only complete half of the SEH theory. And all SEH Liaons are from his guidance; I would like to thank Scott Meyers master. I am watching his book leader; thank you adamc / darwin / julian, of course, there is also Nick Coffee
Content Guide: (Please open the document structure diagram to read this article.) This article includes 2 large exceptions: C standard exception and She exception.
C standard exception: Maybe we have learned him, but you have considered it, in fact, you will not use it, you don't believe, then I ask you: How to achieve garbage recycling in C ? In fact, it doesn't need to be realized, C has already, but you will not use it, then throw out from
She is abnormal: I have to ask you, are you a Win32 programmer? If not, then maybe you really don't need to see this content, SHE is Windows structured exception, each Win32 programmer should have it. The SHE is powerful, including the two parts of Termination Handling and Exception Handling, strongly maintaining the robustness of the code, although sacrifices to some system performance (actually avoid). There is a lot of code in She, and it has been tested on the WIN platform. Here is to mention: The compiler is involved in the vast majority of work in __finally processing, and Exception is OS to take over almost all work, maybe I have not mentioned: For __finally, when encountered Exitthread / FINALLY blocks are not executed when EXITPROCESS / ABORT functions. In addition, our code uses software exceptions than Return Error Message Good 2 ** 32.
In addition, "Using the Handling Function Active Resource Leak" This node references the Terms of More Effective C 9, with 2 colons, tells us that we generally make mistakes, often this mistake is that we don't realize but indeed Will bring our software to bring fatal Leak / Crash, but this is a solution, that is, using "dexterity pointer". If the 37 terms of
C abnormal C introduces an abnormal reason C new abnormal mechanism changes certain things, these changes are thorough, but these changes may make us uncomfortable. For example, the use of unreated Pointer is very dangerous, Memory / Resource Leak has become more likely (don't say what Memory is cheap, it is not a good programmer.), Write a behavior with you The structural function and the destructive function have changed difficult (unpredictable), of course, most dangerous may be the stuffy shit we wrote, or slowing down.
Most programmers know howto use exception to handle our code, but many people don't pay attention to abnormal processing (many of the foreign codes have been treated very well, Java's Exception mechanism is very good). The abnormal handling mechanism is a good way to solve certain problems, but it also introduces many hidden control flows; sometimes it is not easy to use it correctly.
After the exception is throw, there is no way to make the software's behavior predictability and reliability (this sentence is not what I said, is the Coping with Exception and Herb Sutter written by Jack Reeves written for Exception-Safe Generic Containers Middle.) A program without an abnormal safety design is normal, it is a dream, don't think that there is no abnormal possible possible,
For the C procedure, use Error Code, why should I introduce an exception? Because abnormal cannot be ignored. If a function represents an abnormal state by setting a status variable or returning an error code, there is no way to ensure that the function caller will definitely detect the variable or test error code. The result process will continue to run from the abnormal state it encounter, and the exception is not captured, and the program will immediately terminate execution. In the C program, we can use int setjmp (jmp_buf env); and void longjmp (int value); these two functions are completed and exceptionally handled, but the MSDN is described in C to use longjmp in C . Adjusting Stack When you call a destructive function, it is important to the C programs (I usually put the DELETE of the object in the destructor). So we need a method: 1 can notify the abnormal state, can't ignore this notification, 2 and searching the stack to find the exception code, 3 Also make sure that the destructor of the local object is called by Call. The exception handling of C is just to solve these problems.
Some places can only solve the problem, for example, in the current context environment, unable to capture or determine the wrong type, we have to use an exception to go to a larger context environment. Also, the use of abnormal processing, allows the error handler to divide the "usual" code, making the code more concise and more flexible. In addition, the procedure must be essential, and abnormal handling often plays an important role.
C uses the throw keyword to generate an exception, the TRY keyword is used to detect the block, the catch key is used to fill in the exception handling code. An exception can be generated by an object of a certainty or derived class. C can release the stack and clear all the objects in the stack.
C exceptions and PASCAL are different, which is to be implemented by the programmer, and the compiler will not do too much action.
Throw Abnormal class programming throws an exception with throw, such as: Throw ExceptionClass ("My Throw");
In the example sentences, ExceptionClass is a class that is made as a parameter in a string. That is, when Throw, the C compiler first constructs an ExceptionClass object, allowing it to throw the value of the throw. At the same time, the program returns, and the destructor is called. Look below: #include
Void main () {ExceptionClass E ("test"); try {e.mythrow ();} catch (...) {cout << "********* << endl;}} Yes Output Information: Construct TestConstruct My ThrowDestruct My Throw *************** DESTRUCT My Throw (here is a destruct Test === ==================================== However, in general, we may be more accustomed to generating an abnormal statement. And the exception class to throw is divided into different classes. The following code can be we prefer: ........ ..class exceptionclass {public: exceptionclass (const charult class ") {cout < << EXCEPTION CLASS CONSTRUCT STRING << Endl;} ~ ExceptionClass () {cout << "Exception Class Destruct String" << Endl;} void reporterror () {cout << "Exception Class :: this is report error message" << Endl;}}; class arguclass {char * name; public: arguclass (char * name = "default name") {cout << "construct string ::" << name << endl; this-> name = Name } ~ Arguclass () {cout << "destRuct string ::" << name << endl;} void mythrow () {throw exceptionclass ("my throw");}}
_tmain () {Arguclass E ("haha"); try {e.mythrow ();} catch (int) {cout << "if this is message display screen, this is a error !! << endl;} catch (EXCETIONCLASS PTEST) {PTest.ReportError ();} catch (...) {cout << "***************" << endl;}} Output Message: Construct String :: hahaException Class Construct StringException Class Destruct StringException Class :: This is Report Error MessageException Class Destruct StringDestruct String :: haha exception specification using the programming if we call the function of others, an exception is thrown inside, spent viewing the source code to see Do you have any abnormal throws? This will be very cumbersome. A preferred solution is to write a function with an abnormally thrown function, which uses an exceptional specimen that makes us see which abnormality appears.
The abnormal specification is generally the following format:
Void ExceptionFunction (argument ...) throw (ExceptionClass1, ExceptionClass2, ....)
All anomaly classes have been explained in the parentheses of throw () at the end of the function, so that the function caller is a clear.
Note the following form:
Void ExceptionFunction (Argument ...) throw ()
It indicates that there is no exception thrown.
Normal Void ExceptionFunction (argument ...) means: may throw any anomalies, of course, may not be abnormal, the meaning is the most widely used.
After an exception capture, you can throw it again, use a Throw statement without any parameters. The abnormality in the structure and sectors throws this is the most important place in an abnormal treatment.
First look at a program, if I throw an exception in the place where the constructor is throwing, is this sectarian description? Can you not be released if you don't call?
#include
Class exceptionclass1 {char * s; public: exceptionclass1 () {cout << "exceptionclass1 () << endl; s = new char [4]; cout <<" throw a exception "<< endl; throw 18;} ~ ExceptionClass1 () {cout << "~ exceptionclass1 ()" << Endl; delete [] s;}};
Void main () {Try {exceptionclass1 e;} catch (...) {}}
The result is:
ExceptionClass1 () Throw a Exception
Between these two sentences, we have assigned memory to s, but the memory is not released (because it is released in the destructor). It should be said that this is in line with practical phenomena because the object is not integrity. To avoid this, I think you may say: Avoid the object through its own constructor involves an abnormally thrown. That is, there is neither an abnormally thrown in the constructor, and an abnormality should not be thrown in everything that constructor calls.
However, in C can throw an abnormality in the constructor, the classic solution is to use STL standard auto_ptr. In fact, we can also do this: add one init () in the class; and uninit (); member function is used to make the resource allocation work that is easy to generate errors, while the true constructor is set to NULL, Then invoke init (); and determine that its return value / or capture the exception thrown by INIT (); if init (); fail, uninit () is called in the constructor; and set a flag indicating that the configuration failed. Uninit () in accordance with whether the member is released for resources for NULL.
So what about the situation in the destructor? We already know that after an exception thrown, we will call the destructive function of itself. If there is an abnormality in this parsing function, the existing exception has not been captured, which will cause an exception to capture.
Standard C Abnormal C has its own standard anomaly class.
1 A base class: Exception is the base class of all C abnormalities. class exception {public: exception () throw (); exception (const exception & rhs) throw (); exception & operator = (const exception & rhs) throw (); virtual ~ exception () throw (); virtual const char * what () Const throw ();
2 Did to derive two exceptions:
The logic error of the logic_erro report program can be detected before the program is executed.
The Runtime_erro reports the error of the program running, and can only be detected when running.
The above two have their own derived classes:
3 is derived from logic_erro
Domain_error report violates the pre-condition
INVALID_ARGUMENT indicates an invalid parameter of the function
Length_ERROR pointed out an attempt to generate an object that produces more than NPOS lengths (NPOS is the maximum performance value of Size_t)
OUT_OF_RANGE report parameter crossing
BAD_CAST has an invalid Dynamic_cast expression in the runtime type identification
BAD_TYPEID report has an empty pointer P in expression typeid (* p)
4 is derived from Runtime_ERROR
Range_Error report violates the back conditions
Overflow_ERROR reports an arithmetic overflow
BAD_ALLOC report a storage assignment error
This part of the use of a parallelism to prevent resource leakage is a classic and usual, and most of the following content is obtained from the More Effective C terms.
Suppose you are writing software for a small animal shelter, a small animal shelter is a tissue that helps a puppy kitten looking for the owner. A file established every day, contains information information of the contained animal it managed on the same day, and your job is to write a program to read these files and then properly handle each containment animal (Approode Processing). Completing this program a reasonable way is to define an abstract class, ALA ("Adorable Little Animal", then establish a school for puppies and kittens. A virtual function ProcessAdoption processes individual animals, respectively:
Class ala {
PUBLIC:
Virtual void processadoption () = 0;
...
}
Class Puppy: public ala {
PUBLIC:
Virtual void processadoption ();
...
}
Class Kitten: Public Ala {
PUBLIC:
Virtual void processadoption ();
...
}
You need a function to read information from the file, then generate a Puppy object or Kitten object according to the information in the file. This work is ideal for virtual constructors, which describes this function in detail in Terms 25. In order to complete our goal, we declare the function:
// Read animal information from S and return a pointer
/ / Point to a newly established type object
Ala * Readala (istream & s);
The key part of your program is this function as follows: Void ProcessadOptions (istream & Datasource)
{
While (DataSource) {//, continue to cycle
Ala * Pa = Readala (Datasource); File: // Get the next animal
PA-> ProcessadOption (); file: // Treatment of container animals
Delete Pa; File: // Delete objects returned by Readala
}
}
This function loops traversed the information within the DataSource to process each item it encountered. The only thing to remember is to delete the PS at each cycle end. This is a must because each call readala is built. If you do not delete an object, the loop will generate resource leaks.
Now think about it, if PA-> ProcessAdoption throws an exception, what will happen? ProcessadOptions did not capture exceptions, so exceptions will be passed to the caller of ProcessAdoptions. In the transfer, all the statements after calling the PA-> ProcessAdoption statement in the ProcessadOptions function are jumped, which is that PA is not deleted. As a result, any time PA-> ProcessadOption throws an exception causes ProcessAdoptions memory leakage.
Blocking leaks is easy,
Void ProcessadOptions (istream & Datasource)
{
While (Datasource) {
ALA * PA = Readala (Datasource);
Try {
PA-> ProcessadOption ();
}
Catch (...) {// captures all exceptions
Delete Pa; // Avoid memory leakage
// When an exception is thrown
Throw; // Transport anomalies to the caller
}
Delete Pa; // Avoid resource leakage
} // When there is no abnormality thrown
}
But you must use TRY and CATCH to make small changes to your code. More importantly, you must write double clear code, one for normal run, one is prepared for an exception. In this case, two DELETE codes must be written. Like other duplicate code, this code writes upset and difficult to maintain, and it seems to have problems. Whether we let processadOptions returning normally or throw an exception, we need to delete PA, so why do we have to write a delete code in multiple places?
We can put the total clear code in the ProcessAdOptions function in the destructive syndrome, which avoids repeated writing clear code. Because the local object is always released when the function returns, regardless of how the function exits. (Only one exception is when you call longjmp.longjmp's shortcomings is the main reason for C to support exception processing)
The specific method is to use an object instead of the pointer PA, and the behavior of this object is similar to the pointer. When the Pointer-Like object is released, we can call DELETE in the destructor. The object of replacing the pointer is called a Smart Pointers, which explains the following, you can make the Pointer-Like object very dexterously. Here, we don't need such a smart pointer, we only need a Pointer-Lik object that knows the object that deletes it points to the object when it leaves the living space.
It is not difficult to write such a class, but we don't need to write yourself. The standard C library function contains a class template called Auto_PTR, which is what we want. In each Auto_PTR class constructor, let a pointer points to a heap object and delete this object in its destructor. The following is some of the important parts of the Auto_PTR class:
Template
Class auto_ptr {
PUBLIC:
Auto_PTR (T * p = 0): PTR (P) {} // Save PTR, point to object
~ Auto_PTR () {Delete Ptr;} // Delete the PTR pointing to the object
Private:
T * ptr; // raw ptr to object
}
The full code of the AUTO_PTR class is very interesting, and the simplified code implementation cannot be applied in practice. (We must at least add the copy constructor, assign the operator, and the Pointer-Emulating function that will be told below), but the principles behind it should be clear: replace the RAW pointer with the auto_ptr object, you will no longer be a heap object Can't be deleted, even when you throw an exception, the object can be deleted in time. (Because Auto_Ptr's destructor uses delete in single object form, Auto_Ptr cannot be used to point to the pointer of the object array. If you want Auto_PTR to be similar to an array template, you must write one. In this case, Vector instead of Array may be better)
auto_ptrtemplate
Void ProcessadOptions (istream & Datasource)
{
While (Datasource) {
Auto_Ptr
PA-> ProcessadOption ();
}
}
This version of ProcessadOptions is different from the original ProcessadOptions function. First, PA is declared as an Auto_Ptr
The idea of hiding in auto_ptr is: Store the resource that needs to be released automatically with an object, then rely on the destructive function of the object to release resources, this kind of thinking is not just possible to use on the pointer and can be used in other resources. Release. Think about the function in the GUI program, it needs to create a Window to explicit some information:
// This function will have resource leaks, if an exception thrown
Void DisplayInfo (Const Information & Info) {
Window_handle w (CREATEWINDOW ());
Smooth information in WINDOW corresponding to W
DESTROYWINDOW (W);
}
Many Window systems have a C-Like interface that uses the Like CreateWindow and DestroyWindow functions to get and release Window resources. If information is displayed in W, an exception is thrown, WINDOW corresponding to W will be lost, just like other dynamically allocated resources.
The solution is the same as previously described, establish a class, allowing its constructor to get and release resources with the destructor:
File: // A class, get and release a Window handle
Class windowhandle {
PUBLIC:
WindowHandle: w (Handle) {}
~ Windowhandle () {destroywindow (w);}
Operator window_handle () {return w;} // see below
Private:
WINDOW_HANDLE W;
// The following function is declared as private, preventing multiple window_handle copies
File: // For a more flexible discussion, please refer to the following density pointer
WindowHandle (Const WindowHandle &);
WINDOWHANDLE & OPERATOR = (const windowhandle);
This looks like AUTO_PTR, just assignment operations and copy constructs explicitly disabled (see more Effective C Terms 27), there is an implicit conversion operation to convert WindowHandle to Window_Handle. This capability is very important for using the WindowHandle object, because this means you can use WindowHandle as anywhere like RAW Window_Handle. (See more Effective C Terms 5, know why you should use implicit type conversion operations)
By giving the WindowHandle class, we can rewrite the DisplayInfo function as follows:
// If an exception is thrown, this function can avoid resource leakage
Void DisplayInfo (Const Information & Info)
{
WindowHandle W (CREATEWINDOW ());
Obsessed information in WINDOW corresponding to W;
}
Even if an exception is thrown in the DISPLAYINFO, Window established by CreateWindow can also be released.
Resources should be packaged in an object, follow this rule, you usually avoid resource leaks in the abnormal environment. But if you are allocating a resource, an exception is thrown, what happens? For example, you are in the construnce function of the Resource-Acquiring class. Also if such resources are being released, an exception is thrown, what happens? Constructor requires special techniques for constructor and destructive functions. You can get relevant knowledge in More Effective C Terms 10 and More Effective C Terms 11.
Throw an unusual behavior personally think that the next part is actually very classic, which is very helpful to understand the abnormal behavior / abnormal copy.
Terms 12: Understanding the difference between "throwing an exception" and "transmitting a parameter" or "call a virtual function"
From the grammatical point of view, declare the parameters in the function and declare the parameters in the Catch clause. There is almost no difference:
Class Widget {...}; file: // A class, what kind of specifically / / is not important here (Widget W); // Some functions, the parameters are Void F2 (Widget & W), respectively; / / Widget, widget &, or void f3 (const widget & w); // widget * Type VOID F4 (Widget * PW); Void F5 (Const Widget * Pw); Catch (Widget W) ... file: // Some Catch The clause, used to catch (widget & w) ... file: // capture exceptions, exception type is Catch (const widget & w) ... // widget, widget &, or catch (widget * pw) ... / / Widget * catch (const widget * pw) ...
You may therefore think that it is basically the same in the Catch clause with throw to the CATCH clause. There are some same points in this, but they also have a huge difference. Let us talk from the same point. The way you transfer function parameters and abnormal path can be a pass value, pass reference, or transfer pointer, which is the same. However, when you pass parameters and anomalies, the operation process to be completed is completely different. The reason for this difference is that when you call the function, the program's control will eventually return to the call of the function, but when you throw an exception, the control will never return to the abnormality.
There is such a function, the parameter type is widget, and throws an exception of a Widget type:
// A function, read the value from the stream to Widget ISTREAM Operator >> (ISTREAM & S, Widget & W); void passandthrowwidget () {widget localwidget; cin >> localwidget; file: // Pass Localwidget to Operator >> throw localwidget ; // Throw LocalWidget Anomaly} When passing the localwidget to the function operator >>, no copy operation, but point the reference type variable in Operator >> to the localwidget, any of the operations of W is actually applied to the localwidget . This is very different from the throwing LocalWidget exception. Regardless of whether the value is captured through the value or by reference capture (cannot capture this exception through the pointer, the copy operation of the LCALWIDGET will be performed by the type mismatch), and it is said that the copy to the CATCH clause is the copy of the localwidget. It must be done because the destructor will be called when the LocalWidget leaves the living space. If the LocalWidget itself is passed to the Catch clause, this clause is only a destructed Widget, a Widget's "body". This is not available. Therefore, the C specification requires that the object that is abnormally thrown must be copied.
Even if the object thrussed is not released, copy operation is also possible. For example, if the PassandThrowWidget function declares that localwidget is a static variable (STIC),
Void passandthrowwidget () {static widget localwidget; // is now a static variable (STIC); file: // has always been to the program end cin >> LocalWidget; // Run throw localwidget in the past; // will still be pair LocalWidget} file : // Copying operation When the abnormality is thrown, a copy of the LocalWidget will be copied. This means that even if it captures exceptions by reference, it cannot modify the localwidget in the catch block; only the copy of the LocalWidget can be modified. Forced copy copies of the abnormal objects, this restriction helps us understand the second difference between parameters transfer and throwing abnormal: throwing an exception running speed is slower than parameters.
When an exception object is copied, the copy operation is done by the copy constructor of the object. The copy constructor is a copy constructor of the static type of the object, rather than the dynamic type of the object (Dynamic Type) copy constructor. Following this example, with several changes of passAndThrowWidget: class Widget {...}; class SpecialWidget: public Widget {...}; void passAndThrowWidget () {SpecialWidget localSpecialWidget; ... Widget & rw = localSpecialWidget; // rw reference SpecialWidget throw Rw; file: // It throws an exception of Widget //} The exception object thrown here is a widget, even if the RW reference is a SpecialWidget. Because the static type of RW is Widget, not the SpecialWidget. Your compiler does not have the main to RW references to a SpecialWidget. The compiler is noted that the static type of the RW. This behavior may be different from what you expect, but this is consistent with the behavior of copy constructor in C in other cases. (However, there is a technology that allows you to copy according to the dynamic type Dynamic Type of the object, see Terms 25)
An exception is the copy of other objects, this fact implies how you throw an exception in the Catch block. For example, the two CATCH blocks below, you look like:
Catch (Widget & W) // Captures Widget Exception {... // Treatment Exception Throw; // Re-throw an exception, let it} // Continue to pass Catch (Widget & W) // Capture Widget exception {... // Handling exception throw w; // Passing the captured exception} // Copying these two CATCH blocks in the first CATCH block re-thrown the current captured exception, and the second Catch block re-throws It is a new copy of the current capture exception. If you ignore system overhead generation of additional copies, is there a difference in these two methods?
Of course. The first block re-throws the current exception, no matter what type it is. Especially if this exception starts to be thrown as the SpecialWidget type, then the first block is transferred or the SpecialWidget exception, even if the W's static type is Widget. This is because no copy operation is made when re-throwing anomalies. The second Catch block re-throwing new exception, the type is always widget, because W's static type is Widget. In general, you should use
Throw
To reap out the current exception, because this will not change the type of abnormality being passed, and more efficient, because you don't have to generate a new copy. (By the way, the abnormality generated copy is a temporary object. As explained in Terms 19, the temporary object can make the compiler optimize its survival period, but I think your compiler is hard to do. Because there are few abnormalities in the program, the compiler manufacturer will not spend a lot of energy in this regard.)
Let us test the following three Catch clauses used to capture widget exceptions, exceptions are thrown as the passandthrowwidgetp:
Catch (Widget W) ... // Capture of Abnormal Catch (Widget & W) by passing value Catch (const widget & w) by passing the reference (const widget & w) ... file: // Point Const Quote FILE: // We immediately noticed another difference between passing parameters and passing exception. An object that is abnormal (just explained, always a temporary object) can be captured by ordinary references; it does not need to capture the reference-to-const) by reference to the reference object. In the function call, it is not allowed to deliver a temporary object to a parameter of a non-Const reference type (see Terms 19), but it is allowed in an abnormality.
Let us first return to the test of the abnormal object copy. We know the parameters of the function when using the pass value, we have created a copy of the object (see Effective C Terms 22) and store this copy to the parameter of the function. Similarly, when we pass an exception by passing the value, it is done. When we declare a Catch clause:
Catch (widget w) ... // establishes two copies of the object to be thrown by the value capture, one is a temporary object that must be established by all exceptions, the second is to copy the temporary object into W. Similarly, when we capture anomalies by reference,
Catch (widget & w) ... // captures Catch (Const Widget & W) by reference: // also creating a copy of the object that is thrown by reference: Copy is a temporary object. Reflections When we pass the transfer function parameters, we do not have object copies. When an exception is thrown, the copy number of the system constructed (subsequently fractionated) is thrown to the copy number of the object as the same object to the function as a parameter to the function.
We have not discussed abnormalities through pointers, but through pointers throwing exceptions are the same as passing parameters through pointers. No matter which method is a copy of a pointer being passed. The pointer you can't think of throw is a pointer to the local object because the local variable has been released when an exception exits the living space of the local variable. The CatCH clause will get a pointer to the object that has not been present. This behavior should be avoided when designing.
The object is passed from the functional call to the function parameter and the method used from the abnormal throwing point to the Catch clause. This is only one aspect of the difference between parameter delivery and abnormal transmission, the second difference is in the function call The process of improving the type matches the type between the anomalous and the modified or abnormal capture. For example, SQRT function in the standard math library: double sqrt (double); // from
INT i; double sqrtofi = SQRT (i); there is no doubt, C allows for implicit type conversion from int to double, so in the call of SQRT, i is quietly transformed into double type, and its return value is Double . (For detailed discussion on implicit type conversion, see Terms 5) In general, the Catch clause does not perform such a conversion when matching an abnormal type. See the code below:
Void f (int value) {if (someFunction ()) {// If someFunction () returns Throw value; file: //, throw a shaped value ...}}}}}}}} catch (double d) {/////// Only Double Types of Exceptions ...} ...} Intine thus thrown in the TRY block will not be processed by Double Exception Catch clause. This clause can only capture an exception that is really a double type; does not perform type conversion. So if you want to capture int abnormal, you must use the Catch clause with the INT or INT & parameters.
However, two types of conversion can be performed when an abnormality match is performed in the CatCH clause. The first is the conversion between inheritance and base classes. A Catch clause used to capture the base class can also handle the exception of the derived class type. For example, a Diagnostics Portion (see Effective C Terms 49), for example, the Diagnostics Portion (see Effective C Terms 49).
Capture the runtime_errors anomaly Catch clause to capture the orthomy of the RANGE_ERROR type and the Overflow_Error type, which can receive the root Exception exception Catch clause to capture any of its derived class exceptions.
This type of derivation and base class (inheritance_based) can act on values, references, and pointers:
Catch (Runtime_ERROR) ... // CaN Catch ErroRORS OF TYPECATCH (Runtime_ERROR &) ... // Runtime_ERROR, CATCH (const runtime_error &) ... // Range_ERROR, OR // Overflow_ERRORCATCH (Runtime_ERROR *) ... // can Catch ErrorS of Typecatch (const runtime_error *) ... // runtime_error *, // range_error *, or // overflow_error *
The second is to allow transition from a type of type pointer to a united Pointer, so a Catch clause with a Const Void * pointer can capture any type of pointer type exception: Catch (const void *) ... file: // Capture any pointer type unusual
The last difference between passing parameters and delivery abnormalities is that the CATCH clause matching order always depends on their order that appears in the program. Therefore, a derived class anomaly may be processed with a Catch clause capture of its base class anomaly, even if there is a CATCH clause that can process the derived class abnormality, correspond to the same TRY block. E.g:
Try {...} catch (logic_error & ex) {// This Catch block will capture ... // All logic_error} // Exceptions, including its derived Catch (Invalid_argument & ex) {// This block will never Executed ... File: // Because all} // invalid_argument // exception is captured by the // Catch clause above. In contrast to the above behavior, when you call a virtual function, the called function is located in a class that is the dynamic type of the object that is called. You can say that the virtual function uses the optimal fit, and the abnormality processing is the first suitable method. If a Catch clause of a derived class exception is located in front of the Catch clause that processes the base class exception, the compiler will warn. (Because such code is usually illegal in C .) But you'd better do pre-prevention: Do not put the CATCH clause that processes the base class abnormally in front of the CATCH clause that deals the derived class exception. I should write this like this:
Try {...} catch (invalid_argument & ex) {// Handling Invalid_Argument ... file: // Abnormal} catch (logic_error & ex) {// Handling all other ... // logic_errors exception} Summary, Passing an object to a function or an object calling the virtual function and throws an object as an abnormality, there is three main differences between this. First, the abnormality object is always copied at the time of transmission; when captured by a pass value, the abnormal object is copied twice. The object does not need to be copied as a parameter. Second, the object is performed as an abnormality to be transmitted to the function as a parameter, the former type conversion is less than the latter (the former has only two conversion forms). The last point, the order in which the Catch clause performs an exception type is the order in which they appear in the source code, and the first type matches the successful CATCH will be used to execute. When an object calls a virtual function, the selected function is in the best class that matches the object type, even if the class is not the forehead in the source code.
The deximant pointer used to use the smart pointer when writing ADO code, used to COM_PTR_T smart pointer; but always impressed is not very deep; in fact, the dexterity pointer is very good, and the garbage collection, ATL, etc. will be used It has deliberately add this node after the Terms of More Effective, not only wants to introduce it in an exception handling effect, but also want to help with other types of types of codes. Smart Pointer is actually not a pointer, in fact, a form of classes. However, its specialty is the pointer in the C / C , so it is called Pointer. So I hope everyone must remember two points: Smart Pointer is a class rather than pointers, but specialty is imitation pointer.
How do you do it like a pointer? C template technology and operators overloaded a lot of space. First, Smart Pointer must be highly type Strongly typed, template to this function; secondly need to imitate the main two operators -> and *, then operate the operator overload.
Detailed implementation: Template
Example: STD :: auto_ptr in the C standard library is an example of a wide application. Its implementation is different in different versions of STL, but the principles are the same, probably the following look: Template
In the process of using the Smart Pointer, you should pay attention to the problem: there are different considerations for different Smart Pointers. For example, auto_ptr, you cannot use it in a standard container because it only keeps a instance in memory. Grasp the two principles I said earlier: Smart Pointer is a class rather than a pointer, which is an imitation pointer, then everything is good. For example, Smart Pointer as a class, then the following practices may have problems. SmartPtr P; if (p == 0) if (! P) if (p) is clear, P is not a real pointer, which may be wrong. The SMARTPTR design is also very important. You can judge a BOOL SMARTPTR :: null () const. It is also possible if you persist in the form of the above. We will add Operator void * () Try: Template
Win structured abnormality should be understood by people using Win32 platforms. Windows structured exception is part of the operating system, and C exception is only part of C , when we write code with C , we choose C standard exception (you can use MS VC's exception), the compiler will automatically Our C standard is converted to SEH exception.
Microsoft's Visual C also supports the exception handling of C , and uses the function of structured abnormal processing that has been introduced into the compiler and Wi N D O W S operating system internally.
S e h actually contains two main functions: terminalling and abnormal processing (E X C E P Ti O NH a N D L I n g).
In the MS VC's FAQ, there is a part of the SHE, which is abstracted here: "In VC5, add new / EH compilation options to control C abnormal processing. C synchronous exception processing (/ EH) makes compilation Energy can generate less code, / EH is also the default model of VC. "
Be sure to remember the things behind: When using She, the compiler and the operating system are directly involved in the execution of the program code. Another article I wrote by Win32 anomaly event: Memory processing and DLL technology also involve an exception handling in She.
EXCEPTION is divided into software and hardware Exception. Such as: an invalid parameter or 0 divorted software Exception, and access a page that has not been commit will cause hardware Exception. When an exception occurs, the execution process is terminated, and the control is transferred to the operating system, and the OS will use context (Context) The structure saves the current process state, and then starts SEARCH a component that can handle Exception. Search ORDER is as follows: 1. First check if there is a debugger to connect with the EXCEPTION process, whether this debugger is capable Processing 2, if the above cannot be completed, the operating system is in the thread of the Exception Event, the Search Exception Event Handler3, the Search is associated with the debugger 4, and the system performs its own Exception Event Handler Code and Terminate Process ends the handler to use SEH You can completely don't consider whether there is a mistake in the code, so that the main work is separated from the error. Such separation allows you to concentrate on working in front of you, and put the possible errors in the back process.
Microsoft introduced S e h in Wi N D O W s is to facilitate the development of the operating system itself. The developer of the operating system uses S e h to make the system stronger. We can also use S e h to make our own procedures stronger.
The burden caused by S e h is mainly borne by the compilation program, not by the operating system. When an exception block occurs, the compiler is generated for special code. The compiler must generate some tables (T A B L E) to support the data structure of processing S e h. The compiler must also provide a callback (C A L B A C K) function, the operating system can call these functions to ensure that the exception block is processed. Compile programs are also responsible for preparing stack structure and other internal information for operating system usage and reference. Increasing S e h support in the compiler is not an easy task. Different compilerists will implement S e h in different ways, which is not surprising. Fortunses, we can use only the compiler's implementation details, but only use the S E H function of the compiler. (In fact, most compiler vendors use Microsoft's Suggestive Syntax)
End Processing Code Initial One End Processor ensures that a code block (end processing program, Termination Handler) is went, regardless of how the other code (protective body, Guarded body) is exited. The syntax structure of the end handler is as follows: __ try {file: // Protect block} __ finally {file: // End Processor} In the above code segment, the operating system and compiler jointly ensure that __f INally in the handler The code block can be executed, regardless of how the protector (TRY block) exits. Regardless of using R e t u R n in the protective body, or G O T O, or longjump, end the processing program (F i n a l y block) will be called.
===================== ********************** We come to see a real Column: (return value: 10, no Leak, performance consumption: small) DWord func_sehterminateHandle () {dWord dwreeturndata = 0; handle hsem = null; const char * lpsemname = "termsem"; hsem = createMaphore (null, 1, 1, LPSemname); __ try {WaitForsingleObject (hsem, infinite); dwreturndata = 5;} __finally {ReleaseSemaphore (HSEM, 1, NULL); CloseHandle (HSEM);
Dwreturndata = 5; Return dwretata;} This code should only be a basic function, we will modify it later to see the end handler: ============= ====== in the code plus: (return value: 5, no Leak, performance consumption: middle) DWORD func_sehterminateHandle () {dWord dwreturndata = 0; handle hsem = null; const char * lpsemname = "termsem"; HSEM = CreateSemaphore (NULL, 1, 1, LPSEMNAME);
__Try {WaitForsingleObject (HSEM, Infinite); dwreturndata = 5;
Return dwreturndata;} __finally {ReleaseSemaphore (HSEM, 1, NULL); CloseHandle (HSEM);
DWRETURNDATA = 5; Return dwreturndata;} Add a R e T U R n statement at the end of the T R Y block. This R e t U R n statement tells the compiler that you want to quit this function and return the content of the D w Te m P variable, and the value of this variable is 5. However, if this R e t U R n statement is executed, the thread will not release the beacon, and the other threads can no longer receive the control of the beacon. It is conceivable that such execution will produce a big problem, and those threads waiting for the beacon may never resume execution.
By using the end processing program, it is possible to avoid premature execution of the R e T U R n statement. When the R e t U R n statement is attempting to exit the T R Y block, the compiler is to ensure that the code in the F I n a L Y block is first performed. To ensure that the code in the F i n a l L Y block is executed before the R e t U R n statement in the T R Y block exits. In the program, the call to RE L E A S e m a P H O R E is placed in the end processing block, and the guarantee beacon will always be released. This will not cause a thread to occupy a beacon, otherwise it will mean that all other threads waiting for the beacon will never be assigned C P U time.
After execution of the code in the f i n a l l y block, the function is actually returned. Any code that appears under the F i n a L Y block will no longer be executed because the function has been returned in the T R y block. So the return value of this function is 5, not 10. The reader may ask the compiler how to ensure that F i n a l y is executed before the T R Y block can exit. When the compiler checks the source code, it sees RE T u R n statements in the T R Y block. In this way, the compiler is generated to save the return value (this example 5) in a temporary variable established in a compiler. The compiler then reproduces code to execute the instructions contained in the F i n a L Y block, which is called local expansion. More special cases, due to the prematured exit code due to the T R Y block, thereby generating local expansion, resulting in a system to perform content in the F i n a L Y block. After execution of the instructions in the f i n a l L Y block, the value of the temporary variable of the compiler is removed and returned from the function.
It can be seen that to complete these things, the compiler must generate additional code, and the system should perform additional work. The steps required to end the treatment are also different on different C P u. For example, on the A L P H A processor, hundreds of or even thousands of C P U instructions must be executed to capture premature returns in the T r y block and call f i n a L Y block. When writing a code, you should avoid premature exiting in the TR Y block of the end handler because the performance of the program will be affected.
Later, the _ _ L E A V E keyword will be discussed, which helps to avoid writing code that causes local expansion.
The purpose of designing abnormal processing is to capture abnormalities - an abnormal situation of uncommon syntax rules (in our example, it is too early). If the situation is normal, it is more effective to capture common things to capture common things than to rely on the SE H function that relies on the operating system and compiler. Note When the control flow naturally leaves the T R Y block and enters the F I n a L Y block (just in F u N c E N S TET E I N, the system overhead of entering the F I n a L Y block is minimal. Using Microsoft's compiler on the x86 CPU, only one machine command is executed when performing a block of the TRY block, and the reader can notice this system overhead in its own program. When the compiler is to generate additional code, the system overhead is worth noting when the system is executed, and the system overhead is worth paying ======================== Modify code: (Return value: 5, no leak, performance consumption: middle) dword func_sehterminateHandle () {dWord dwreturndata = 0; handle hsem = null; const char * lpsemname = "termsem"; hsem = createMaphore (null, 1, 1, lpsemname) ;
__Try {WaitForsingleObject (HSEM, Infinite); dwreturndata = 5;
IF (dwreturndata == 5) GOTO RETURNVALUE;
Return dwreturndata;} __finally {ReleaseSemaphore (HSEM, 1, NULL); CloseHandle (HSEM);
DWRETURNDATA = 5; RETURNVALUE: RETURN DWRETURNDATA;} Code, when the compiler sees the G O T o state in the T R Y block, it first generates a local expansion to perform the contents of the F i n a L Y block. This time, after the code in the f i n a l L Y block, the code after the R e T U R N Va L u e label is executed because there is no return in the T R Y block and the F I n a L Y block. The code here causes the function to return 5. Moreover, due to the disruption of the natural flow from the TRY block to the FinalLy block, it is possible to suffer a large performance loss (depending on the CPU of the running program) ****************** ****** ===================== Write the above code is initial, and now to see the true value of the end handler in our code: See the code: (the signal light is released normally, the memory of Reserve is not FREE, Security: Security) DWORD TERMHAPPENSOMEERROR () {dWord dwreturnValue = 9; DWORD DWMEMORYSIZE = 1024; char * lpaddress; lpaddress = (char *) Virtualalock NULL, DWMEMORYSIZE, MEM_RESERVE, PAGE_READWRITE); * LPADDRESS = 'a'; // <- here, will happen a access exception!
Virtualfree ((LPVOID) LPADDRESS, 1024, MEM_RELEASE; RETURN DWRETURNVALUE;}
DWord func_sehterminate () {dWord dwreturndata = 0; handle hsem = null; const char * lpsemname = "termsem"; hsem = createMaphore (NULL, 1, 1, LPSEMNAME);
__try {WaitForSingleObject (hSem, INFINITE); dwReturnData = TermHappenSomeError ();} __finally {ReleaseSemaphore (hSem, 1, NULL); CloseHandle (hSem);} return dwReturnData;} In the above code, we see TermHappenSomeError () function Contains an error (submitting a page without a commit), which causes an invalid memory access. If there is no S e h, in this case, a very common Application ERROR dialog will be displayed. When the user ignores this error dialog, the process is over. When this process ends (due to an invalid memory), the beacon will still be occupied and will never be released. At this time, the threads in any other process waiting for the beacon will not be assigned C P U time. However, if the call to R e l e a s e s e m a p H O R E is placed in the F I n a L Y block, the beacon can be guaranteed to be released, even if some other functions can cause memory access errors.
If the end handler is strong enough, you can capture the process of ending due to invalid memory access, we can believe that it can also capture the combination of S e t J u m p and L O N g J u M P, as well as those simple statements such as B R E A K and C O N T I n. ======================= The following is a very challenging code, but this code is in real-performance programming, I believe that there is no programmer to write this. Bored code segment. Code: (Return Value: 14) DWORD FUNC_SEHTERMINATEHARD () {DWORD DWTEMP = 0; while (dwtemp <10) {__TRY {if (dwtemp == 2) Continue; if (dwtemp == 3) Break;} __finally {dwtemp ; } DWTEMP ;} dwtemp = 10; Return Dwtemp;} We have made what the function has done step by step. First, D w te m p is set to 0. The code in the T r y block is executed, but the value of the two I F statements is not T R u E. Execute the code naturally moved into the f i n a L Y block, where D w Te m P increases to 1. Then the instructions after the F I n a l L Y block increase D w te m p, making it a value of 2, when the loop continues, the C O N t I n, the C O N t I n, in the T R Y block, will be executed. If there is no end handler to force F I N a L Y block before exiting from the T R Y block, immediately jump back to W H i L E test, D w Te m p will not be changed, unlimited (dead) cycles will appear. Using an end processing program, the system knows that the C O N T I n is to cause the control flow to exit the T R Y block, and will move to F i n a L Y block. In F i n a l L Y block, D w te m p is added to 3. However, the code after the f i n a l L Y block is not executed because the control flow returns to C O N t i n u e, and then the beginning of the loop. Now we handle the third repetition of the loop. This time, the value of the first I F statement is Fa L s E, but the value of the second statement is T R u E. The system can also capture attempts to jump out of the T R Y block, and execute the code in the f i n a l y. Now D w te m p increases to 4. Since the B R e a k statement is executed, the control recovery of the program portion after the loop. Thus, the code in the cycle after the F i n a l L Y block is not executed. The code under the loop adds 1 0 to D w Te m P, when the value of D w Te m P is 1 4, which is the result of calling this function.
Although the end handler can capture most of the cases of TR Y blocks extends too early, it cannot cause code in the f i n a l y when the thread or process is ended. When the e x i t t h r e or e x i t p r e A D or E x I t p R O C E S is, the thread or process will be immediately ended without executing any code in the F i n a L Y block. In addition, if a program call TE R m i n a t e t, TE R, TE R M, the thread or process will die, and the code in the f i n a L Y block is not executed. Some C run functions (e.g., A B O R t) to call e x i t p r o c e s s, and the code in the f i n a l L Y block cannot be executed. Although there is no way to prevent other programs from ending your threads or processes, you can prevent you from using E x i t t h r e a d and e x i t p R O c e s s. ======================== Go to our basic function, we modify it, the next code is: (return value: 100) DWord Func_sehterminateMReturn () {DWORD dwreturndata = 0; handle hsem = null; const char * lpsemname = "termsem"; hsem = createMaphore (NULL, 1, 1, LPSEMNAME);
__Try {WaitForsingleObject (HSEM, Infinite); dwreturndata = 5; return dwretotata;} __finally {ReleaseSemaphore (HSEM, 1, NULL); CloseHandle (HSEM); Return (DWORD) 100;}
Dwreturndata = 10; Return dwretata;} T R Y block will be executed, and try to return DWRETURNDATA value (5) to the caller of this function. As mentioned earlier, trying to return from the T R Y block will result in generating code, put the return value in a temporary variable established by the compiler. Then, the code in the f i n a l l y block is executed. Here, a R e T U R n statement is added to the f i n a L Y block. So returning 5 or 1 0 0? The answer here is 1 0 0, due to the RE T u R n statement in the F I n a L Y block, the value of 1 0 0 is stored in the temporary variable stored in the value 5, covering the value 5. When the F I n a L Y block is executed, the value (1 0 0) in the temporary variable is now returned to the calling program.
We have seen that the end handler is effective in remedy the implementation of premature exiting in the T R Y block, but it also sees that the end handler has generated the results we don't want to have problems in the premature exit of the T R Y block. A better way is to avoid any statements that will cause premature exiting in the TR Y block of the end of the process. In fact, it is preferable to remove the RE T U R N, C O N, and the other statement of the end processing program from the end processing program, and placed outside the end processing program. This will make the compiler generate less code because it is not necessary to capture premature exit in the T r y block, and the compiler produces faster code (because the execution of local expansion is small). In addition, the code is easier to read and maintain the application end handler programming We have already talked about the basic grammar and language of the end handler. Take a look at how to simplify a more complex programming problem with the end handler. First look at a function that has not used the end handler: (~~~~~~~~~~~~ big brother, I really can't eat, so I will not come up. I just talk about it here. Notes and programming specifications.)
In the programming, many people often like to use the wrong judgment. The consequences of this are very poorly readable, and the code is difficult to maintain, so the real benefit of using the end handler is all the cleaning (Cleanup) code of the function. In one place and is only in one place: Finally block. If you need to add the conditional code in this function, simply add a cleaning line in the f i n a l y block, you don't need to return to each possible failed place to add cleanup code.
But we have discovered and confirmed that if we use the jump statement such as Return in the __Try block, the system has been generated. To help avoid using R e t U R n statements in the T R Y block, Microsoft adds another keyword __l e a v e in its C / C compiler. (See MSDN)
Use the L E a V E keyword in the T R y block to cause the end of the T R Y block. It can be considered to jump to the right braces of the T R Y block. Since the control flows naturally exits from the T R Y block and enter the F I n a L Y block, no system overhead is generated. When using the end handler to design a function in this manner, to remember to initialize all resource handles as invalid values before entering the T R Y block. Then, in the F i n a l y, see which resources are successfully allocated, you can know what to release. Another way to determine the need to release resources is to set a flag for successful allocation resources. The code in the f i n a l l Y block can then check the status of the flag to determine if the resource is to be released.
Summary of Finally Block Description We have clearly distinguished two cases of enforcing F i n a l y: • The normal control flow of F i n a L Y block from the T R y block. • Local development: For premature exit from the T R Y block (G O TO, L O N G J U M P, C O N T I N E, B Rs, etc.) forced control to transfer to F I n a L Y block. In the third case, global unwind, there is no obvious identification when it happens, and we have seen in the FUNC_SEHTERMINATE function in this chapter. In the TR Y block of FUNC_SEHTERMINATE, there is a call to the TermhappensomeError function. The TermhappensomeError function causes a memory access violation, a global expansion to perform FUNC_SEHTERMINATE function's F i n a l y block. Due to the above three cases, the code in the F i n a l y is executed in the f i n a l y block. To determine which case causes F I n a l y block execution, the internal function AbnormalterMination: This internal function is only called in the F i n a L Y block, returns a B O O L E a n value. It is pointed out whether the T r y block combined with the F I n a l L Y is prematurely extends. In other words, if the control flow leaves the T r y block and naturally enters the F I n a L Y block, AbnormalterMination will return Fa L S E. If the control stream is not normal to exit T R Y block - typically due to local expansion caused by G O T O, R e T U R N, B R E A K, or C O N T, or due to memory access violations or other exceptions - the call to AbnormalterMination will return T R u E. There is no way to distinguish the implementation of the F I n a l L Y block is due to the global development or due to local development. But this usually does not become a problem, because you can avoid writing code to perform local expansion. (Note the internal function is a special function for compiler identification. Compiler is an inline code to generate an inline code rather than generating a call function. For example, Memcpy is an internal function (if specified / o i compile program switch When the compiler saw a call to Memcpy, it directly inserts Memcpy's code into a function of Memcpy, rather than generating a call to the Memcpy function. The role is the length of the code, but the execution speed is accelerated. .
Before proceeding, review the reason for the use of the end handler: • Simplify the error handling, because all cleaning work is in one position and is guaranteed. • Improve the readability of the program. • Make the code easier to maintain. • If you are used, you have the smallest system overhead.
The exception handler is an exception that we don't want some incident. When writing a program, the programmer does not want to access an invalid memory address or use 0 to divide a value. However, such errors often happen. C P u is responsible for capturing invalid memory access and using 0 divisions, and trigger an exception as a reaction to these errors. The abnormality caused by C P u is the so-called hardware exception. Behind this chapter, we will also see that the operating system and applications can also trigger the corresponding exception, called Software Exception.
When a hardware or software exception occurs, the operating system provides an opportunity to provide an opportunity to investigate what type of exception is triggered and allows the application to handle exceptions. Below is the syntax of the exception handler: __ try {file: // Protect block} __ except (exception shark) {file: // exception handler} pay attention __ e x c e p t keyword. Whenever you create a T R Y block, it must follow one F i n a l y block or an E x C e p t block. After a TRY block, there is no existing f i n a l y, and there is an E x C e p t block. However, it can be nested in the T R Y - E X C e p t block, but it can be in turn. Exact Processing Code Presidential and End Processing Different, Exception Filter and an exception handler are directly executed by the operating system, and the compiler does not do anything in the calculation of an abnormal filter expression and perform an exception handler. The following sections describe the normal execution of the T R Y - E X C e p t block, explaining how the operating system and why the exception filter is calculated, and the environment where the operating system performs an exception handler.
I originally wanted to write all the code, but it was too long to write this side, so it was just to explain it, and TRY and Except block were relatively simple.
Although RE T u R N, G O T, G O, C O N, G O, C O N, G O N, C O N, G O, C O N, which is highly opposed, in the T R Y block of the end processing program, but uses these statements in the TR Y block of the exception handler to generate a poor impact of speed and code scale. Such statements do not cause local expansion system overhead in the T r y block combined with the E x C e P T block
When an exception is thrown, the system will be positioned to the beginning of the Except block, and the value of the exception filter expression is calculated, and the result value of the filter expression can only be one of the three identifiers below, which defines in Windows. EXCEPT. H file. Identifier is defined as:. EXCEPTION_CONTINUE_EXECUTION (-1) Exception is dismissed Continue execution at the point where the exception occurred.EXCEPTION_CONTINUE_SEARCH (0) Exception is not recognized Continue to search up the stack for a handler, first for containing try-except statements. , then for handlers with the next highest precedence.EXCEPTION_EXECUTE_HANDLER (1) Exception is recognized. Transfer control to the exception handler by executing the __except compound statement, then continue execution at the assembly instruction that was executing when the exception was raised.
The following will discuss how these identifiers change the execution of the thread. The following process sumbs into how the system handles an exception: (the process assumes here is forward) ***** Start -> Perform a CPU instruction -> {Does there is an abnormally trigger} -> Yes -> system determination The prosthetic TRY block -> {This try block has an Except block} -> Yes -> {Filter expression value} -> Exception execution handler -> Global Expansion -> Except block Code -> Except Block After Performing ***** Exception_execute_handler In the exception filter expression, if it is exception_execute_handler, this value means to tell the system: "I recognize this anomaly. That I feel this An exception may happen at some time, I have written code to handle this problem, now I want to execute this code. "At this time, the system executes a global expansion, then executes the code in the Except block (exception handler code) Jump. After the code is executed in the Except block, the system considers this exception to be processed and allows the application to continue. This mechanism allows Windows applications to grasp errors and handle errors, and then make the program continue to run, and do not need to know the error. However, when the Except block is executed, will the code restore it? Think of thinking, we can think of several possibilities:
The first possibility is to resume execution from the CPU instruction that produces an abnormality. This looks like a reasonable approach, but in fact, many programs write methods make the subsequent instructions not successfully executed when the previous instructions are wrong. The code should be as structured as much as possible so that the CPU instruction after the abnormality is generated, is expected to obtain a valid return value. For example, there may be an instruction allocated memory, and a series of instructions should be performed to perform the operation of the memory. If the memory cannot be assigned, all subsequent instructions will fail, and this program is repeatedly generated. Fortunately, Microsoft did not let the system recover the execution of the instruction from the command of the abnormality. This decision makes us free from the problem facing the above.
The second possibility is to resume execution from an instruction that produces an abnormality. This is very interesting. What happens if there is such a statement in an Except block? With this assignment statement in the Except block, you can resume execution from an unusual instruction. This time, the execution will continue and will not produce other exceptions. You can do some modifications to let the system re-execute instructions that produce exceptions. You will find that this method will lead to some subtle behavior. We will discuss this technology in the Exception_Continue_execution section.
The third possibility is to start recovery from the first instruction after the Except block. This is actually what happened when the value of the exception filter expression is exception_execute_handler. After the code in the Except block is executed, the first instruction recovery after the Except block is controlled.
In now, the software has become more and more, if you don't have SEH, it is impossible to achieve a complete strong application. Let's first look at a template program, that is, the runtime function strcpy: This is a fairly simple function, how can it cause a process end? If the caller passes a NULL (or any invalid address) in these parameters, STRCPY causes an access exception and causing the entire process to end. Use SEH, you can build a completely strong strcpy function: char * RobustStrcpy (char * strDes, char * strSource) {__try {strcpy (strDes, strSource);} __except (EXCEPTION_EXECUTE_HANDLER) {file: // do not do no Everything done by this function is to place the call to StrCPY in a structured exception handling frame. If STRCPY is successful, the function returns. If STRCPY causes an access exception, the thread performs exception handler code. In this function, the handler code is not done, and RobustStrpy just returns to its caller, which will not cause the process to end.
The function in this code is returned to a string in a spiked number: dword func_robustToKens (const char * str) {dWord itokens = -1; char * strTemp = null; __Try {strTemp = (char * ) Malloc (str) 1); strcpy (str); char * psztoken = strtok (strTemp, "); for (; psztoken! = null; psztoken = strtok (null,")) iToKens ; ITOKENS ;} __except (exception_execute_handler) {file: // do nothing for test} free (strtemp);} Seeing this function in a few different situations: First, if the caller passes the function NULL (or any invalid memory address), ITOKENS is initialized into -1. The call to Strlen in the TRY block causes an exception. The exception filter gets control and transfer control to the Except block, and the ExcePt block does not do. After the Except block, free free up the temporary memory block. However, this memory has never been allocated, so the end calling free, pass NULL as a parameter. ANSI C clearly illustrates that free use of NULL as a parameter is legal. At this time, Free is nothing, this is not a mistake. Finally, the function returns -1 and points to failure. Note that the process is not over. Second, the caller may pass a valid address to the function, but the call to Malloc (in the TRY block) may fail and return NULL. This will result in a call to STRCPY to cause an access exception. Similarly, the exception filter is called, the except block is executed (nothing), free is called, passed to it null (nothing), returns -1, tell the calling the program that the function failed. Note that the process has not ended. Finally, assume that the caller delivers a valid address to the function, and the call to Malloc has been successful. In this case, the remaining code will also successfully calculate the number of symbols in the ITOKENS variable. At the end of the TRY block, the exception filter will not be evaluated, and the code in the Except block will not be executed, and the temporary memory buffer will be released and returned to the caller back ITOKENS. It will feel very good to use SEH.
The previous code has no code in __except, now let's look at this code: pbyte func_robustmemdup (Pbyte PBSRC, SIZE_T CB) {pbyte pbdup = null; __Try {PBDUP = (PBYTE) Malloc (cb); Memcpy PBDUP, PBSRC, CB);} __except (exception_execute_handler) {free (pbdup); pbdup = null;} Return PBDUP;} This function assigns a memory buffer and copy bytes from the source block to the destination block. The function then returns the address of the replicated memory buffer to the call (if the function fails) returns NULL). I hope that the calling program will release it when you don't need a buffer. This is the first example in which there is actually code in the Except block. Let's take a look at how this function is executed under different conditions. • If the call program passes an invalid address for the PBSRC parameter, or if the call to Malloc fails (return null), Memcpy will cause an access exception. This access exception performs a filter to transfer control to an Except block. In the Except block, the memory buffer is released, and the PBDUP is set to null to the calling program can know the function failed. Here, note that ANSI C allows the free NULL. • If the call program delivers a valid address to the function, and if the call to Malloc is successful, the address of the new allocated memory block is returned to the midplane expansion when an exception filter is exception_execute_handler, the system must perform a global development ( Global unwind. This global development allows all TRY_FINALLY blocks that start executing but unfinished TRY_FINALLY blocks after processing an exception. Regarding the global development, it is not continuing here (writing this text), I have been on the 4th day, I'm ~), the global development is also called the Try_Final's code or function.
Also, by placing a RETURN statement in the finally block, you can prevent the system from complete a global development. Although MS passed this design, the programmer stopped to stop, so that the code continued, but I have said that try to put the Return statement in the FINALLY block.
Exception_Continue_execution We take a closer look at the exception filter to see how it calculates one of the three exception identifiers defined in ExcPt.h. In the previous code, we directly set the exception to exception_execute_handler, but in fact, we should make the value of the filter according to the specific situation, such as calling a function to determine which identifier should be returned.
An exception occurs, if it is an Exception_Continue_execution flag, the code will try again to execute the exception code (this time __except block is not executed!), So we usually return to an Exception_Continue_execution when using exception_continue_execution Previously, our error code was changed back, otherwise it will enter the dead cycle. The following code would result in an endless loop: DWORD Func_SEHExceptionContinue () {DWORD dwTemp = 0; __try {dwTemp = 5 / dwTemp; dwTemp = 10;} __except (EXCEPTION_CONTINUE_EXECUTION) {MessageBeep (0);} return dwTemp;} in When using Exception_Continue_execution, you must be particularly careful, there is a case that you can success each time you use Exception_Continue_execution: When it is discrete to a storage area to a reserved area. Combine virtual memory technology with structural exception processing, you can write some execution very fast, very efficient programs.
Exception_Continue_SearchException_Continue_Search: Yes tell the system to continue trace the tree to find additional exception filters. (What happens if each filter returns Exception_Continue_Search? In this case, the so-called "unhandled exception"
Everyone will follow the code below to understand: void func_sehpost (char * SZ) {__TRY {* SZ = 0;} __except (Exception_Continue_Search) {file: // hRe never exceuteate cout << "ife message, here is Error "<< Endl;}}
DWORD getExceptionFlitercode (char ** ppszbuffer) {if (* ppszbuffer == null) {* ppszbuffer = g_szbuffer; return exception_continue_execution;} returnction_execute_handler;}
DWORD Func_SEHExceptionSearch () {char * pszBuffer = NULL; __try {Func_SEHPost (pszBuffer);} __except (GetExceptionFliterCode (& pszBuffer)) {cout << "Exception Block Run" << endl;} return TRUE;}
GetExceptionCode must analyze the specific situation before making a value to return. For example, an exception handler may know what to do when an exception caused by 0, but does not know how to handle a memory access exception. The abnormal filter is responsible for checking the actual situation and returning the appropriate value. The following code illustrates a method to point out the category of the abnormality: BOOL FUNC_SEHEXCEPTIONGETCODEBASE () {INT X, Y; __TRY {x = 0; y = 4 / x;} __except ((getExceptioncode () == Exception_int_divide_by_zero ? / Exception_execute_handler: / exception_continue_search) {file: // handle Divide by zero exception}} getExceptionCode Returns a value indicating the type of exception: all predefined exceptions and corresponding meaning, these content take self Platform SDK documentation. These exception identifiers can be found in the WinBase.h file. We have done these exceptions. 1. Abnormal abnormality related to memory • e x C e p T I o N _ a c c e s _ v i o L at I o N. The thread is trying to read or write a virtual address, but there is no appropriate access. This is the most common exception. • e x C e p T I o n_Dat y p e _ m i s a l i g n m e n t. Threads attempt to read or write unlined data on hardware that does not support alignment (A L i g n m e n t). For example, 1 6-bit value must be aligned on the 2-byte boundary, and 3 2 digits should be aligned on the 4-byte boundary. • e x c e p T I o N _ a r r ay _ b O u n d s _ e x c e e D e D. The thread is trying to access an array element, and the corresponding hardware supports boundary check. • e x c e p T I o n _ i n _ pa g e _ e r R o R. Since a file system or a device launchler returns a read error, pages failure that cannot meet the requirements. • e x c e p T I o N _ g u A r d _ Pa g E. A thread attempts to access a memory page with a PA G E_G U A R D protection attribute. This page is accessible and causes an E x C e p t i o n_g u a r d_PA g e exception. • Exception_sta c k _ o v E R f L o W. The thread is used to assign all stack spaces assigned to it. • e x C e p T I o N _ I L E g A L _ i n s T r u c t i o N. The thread performs an invalid instruction. This exception is defined by a particular C P U structure; on different C P u, an invalid instruction can cause a trap error. • e x C e p T I o N _ p R i v _ i n s T r u c t i o N. The thread performs an instruction that is not allowed in the current machine mode. 2. Abnormalities related to abnormal • E X C E P T I o N _ I N VA L i D _ D i S P O S I Ti O N. An exception filter returns a value, this value is not e x c e p μ ie _ E x e c u T E _ H A N D L E R
• e x C e p T I o N _ N o N C O N T I n u A B L E _E X C E P Ti O N. An abnormal filter back to an unusible abnormality returns E x C e p T I o N _ C O N T I n u e _ e x e c u T I o N. 3. Abnormalities related to debugging • Exception_breakpoint. I have a breakpoint. • e x c e p T I o N _ S i n g l e _ S t e P. A track trap or other single-step instruction mechanism tells an instruction to complete. • e x c e p T I o N _ I n va L i d _ h a n d L E. Pass an invalid handle to a function. 4. Exception_int_divide_by_zero is related to an integer. Threads attempt to use integer 0 to except an integer • Exception_int_overflow. The result of an integer operation exceeds the scope of the integer value. 5. Abnormalities related to floating point • E X C e p T I o N _ f LT _ D E N O R m A L _ O P E R A N D. One operand in floating point operation is abnormal. The abnormal value is a very small value and cannot represent a standard floating point value. • Exception_flt _ D i v i d e _ b y _ z e r O. Threads attempt to use floating point 0 to except for a floating point. • Exception_flt _ I n e x a c t _ r e s u lt. The result of floating point operation cannot be accurately expressed as a decimal decimal. • Exception_flt _ i n va L i D _ O P e r at I o N. Indicates that any other floating point exceptions not listed here. • Exception_flt _ O V e R f L o W. The result of floating point operation exceeds the allowable value. • Exception_flt _ S TA C K _ C H. Since the floating point operation causes a stack overflow or underflow. • Exception_flt _ u n d e R f L o W. The result of floating point operation is less than the allowed value. Function getExceptionCode can only call in a filter (in parentheses after _Except), or call in an exception handler. Remember, you cannot call getExceptioncode in an exception filter function. The compiler captures such an error. Also, the above exception code follows the rules for the error code defined in the WINERROR.H file. (I will write a small article after I have shown Winerror.h)
GetExceptionInformation When an exception occurs, the operating system is pressed into three structures to the stack that causes an exception thread, which is: Exception_Record structure, Context structure, and Exception_Pointers structure. The Exception_Record structure contains information about C P u, and the Context structure contains information that has been dependent on C P u. The Exception_Pointers structure has only two data members, both of which are pointers, pointing to the Exception_Pointers and Context structures that are pressed into the stack.
In order to obtain this information and use this information in your own program, you need to call the getExceptioninformation function: This internal function returns a pointer to the Exception_Pointers structure.
About the getExceptioninformation function, the most important thing to remember is that it can only be called in an exception filter because EXCEPTION_RECORD and Exception_Pointers are valid only when processing exception filters. Once the control is transferred to an exception handler, the data in the stack is deleted.
If you need to access these exception information in your exception handler block (although there is very few need to do this), you must save the Context data structure and / or Exception_record data structure to which the Exception_Pointers structure is required to be established in one or more. Variables. The following code shows how to save EXCEPTION_RECORD and CONTEXT data structure: void Func_SEHExceptionGetInfo () {EXCEPTION_RECORD SaveExceptRec; CONTEXT SaveExceptContext; int x = 0; __ try {x = 5 / x;} __except (SaveExceptRec = * (GetExceptionInformation ()) -> ExceptionRecord, SaveExceptContext = * (GetExceptionInformation ()) -> ContextRecord, EXCEPTION_EXECUTE_HANDLER) {switch (SaveExceptRec.ExceptionCode) {file: // Here, Do your thing for code value}
}} The above code: Note the use of C language comma (,) operators in an exception filter. Many programmers are not used to using this operator. It tells the compiler to perform a comma-separated expression from left to right. When all expressions are evaluated, the result of the last (or right) expression is returned.
The following is EXCEPTION_RECORD structure: typedef struct _EXCEPTION_RECORD {DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD * ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; DWORD ExceptionInformation [EXCEPTION_MAXIMUM_PARAMETERS];} EXCEPTION_RECORD; EXCEPTION_RECORD structure contains details about the exception of recent, the information Independent in CPU: • ExceptionCode contains an exception code. This is the same as the information returned by the internal function getExceptionCode. • ExceptionFlags contains a logo for exceptions. There are only two values, 0 (pointing out a continued exception) and E X C O N T I n U A B L E (pointing out an unopentable exception). After an unsuitable exception, to continue, an E x C E P T I o N _ N o N C O N T I n U A B L E_E X C E P T I O N is abnormal. • E x C E P Ti O N R E C O R D is directed to another unprocessed exception E x C E P Ti structure. When processing an exception, it is possible to trigger another exception. For example, the code in the abnormal filter may be divided by zero. When nested anomaly occurs, the exception record can be linked to provide additional information. If an exception is generated during the process of processing an abnormal filter, a nesting exception occurs. If no exception is processed, this member contains a N u L L. • ExceptionAddress points to the address of the C P U command that produces an exception. • N u m b E R P A R A M E T E R S specifies the number of parameters associated with abnormalities (0 to 1 5). This is the number of elements defined in an array of e x c e p t I o n n f O r m a t i o n. This value is zero for almost all abnormalities. • e x c e p T I o n i n f O r m a t i o n The array of additional parameters is used to describe exceptions. For most abnormalities, array elements are undefined. The last two members of the e x C e p T I o n_ r e c O R D structure, N u m b E R p A R A M e T E R S and E X C E P T I O N I N f O R M a T I O N provide some additional information about abnormal filters. There is currently only one type of exception to provide additional information, that is, E X C E P Ti O N _ a C C E S _ V I O L at I O N. All other possible exceptions set N u m b e r p A r a m e t e R s to zero. We can check the array members of e x C e p T I O N i n f O r m a T I O n to view additional information about the abnormality generated. For an E x C E P Ti O N _ a C C E S _ V I O L AT I O N abnormality, E X C E P T I O N i N f O R M A T I O N [0] includes a flag indicating the type of operation that is an operational accessibility. If this value is 0, it indicates that the thread tries to read the data that is not accessible. If this value is 1, it indicates that the thread is to write unacceptable data. ExceptionInformation [1] indicates the address that cannot be accessed.
By using these members, we can construct an abnormal filter to provide a large number of information about the program. Essentially, each available register on the C P u, this structure corresponds to a member. When an exception is triggered, you can find more information by verifying members of this structure. Unfortunately, in order to get this possible benefits, the programmer is required to write code dependent on the platform to confirm the machine running in the program, using the appropriate context structure. The best way is to arrange a # ifdefs instruction in the code. The Context structure of different CPUs supported by Windows defines in the Winnt.h file.
Software is abnormal so far, we have been discussing hardware abnormal, that is, C P u captures an event and triggeting an exception. It can also be forced to trigger an exception in the code. This is also a way to fail to its caller report. Traditionally, failed functions should return some special values to indicate failure. The caller of the function should check these special values and take an alternative action. Typically, this caller wants to clear the do what is done and return it its own failure code to its caller. The layer-by-layer transfer of this error code will make the code of the source program very difficult to write and maintain. Another method is to cause functions that cause an exception when failure. With this method, the code is easier to write and maintain, and it is also performed because it usually does not need to perform those error test code. In fact, the error test code is executed when an exception occurs when the failure occurs. But unfortunately, many developers are not accustomed to using exceptions in error handling. This has two reasons. The first reason is that most developers are not familiar with S e H. Even a programmer is familiar with it, other programmers may not be familiar with it. If a programmer has written a function that triggers an exception, other programmers do not write an S e h frame to capture this exception, then the process will end by the operating system. The second reason for developers does not use S e h is that it cannot be ported to other operating systems. Many companies' products should face a variety of operating systems, so they want to have a single source code as the basis of the product, which is understandable. S e h is a technology specifically for Wi N D O W s.
This paragraph discusses the content related to the error through an abnormality. First, let's take a look at the Windows Heap function, such as HeapCreate, HeapAlloc, etc. Usually when a stack (H E A P) function fails, it returns N u L1 to point out failed. However, these heap functions can be transmitted to the Heap_Generate_Exceptions flag. If this flag is used and the function fails, the function does not return n u L1, but is caused by the function that is an exception in the STATUS_NO_MEMOR software. The other parts of the program code can be used to capture this exception.
If you want to take advantage of this exception, you can write your T R Y block, as if the memory allocation can always succeed. If the memory allocation fails, you can use the E x C e p t to handle this exception, or by matching the T r y block with the F I n a l y block, the function is cleared. This is very convenient.
The method of capturing software abnormalities is identical to capture hardware abnormalities. That is, the content described above can be applied to software exceptions. Here, discuss how to make your own function trigger the software exception, as a method of failing. In fact, you can implement your function with a method similar to the Microsoft implementation heap function: Let the caller pass a flag to tell the function how to indicate failure.
Easy to trigger a software is easy, just call the RaiseException function: raiseExceptionthis function raises an exception in the calling thread.
Void RaiseException (DWORD DWEXCEPTIONCE, DWORD DWEXCEPTIONFLAS, CONST DWORD * LPARGUMENTS; the first parameter d w e x C E P T I O N C O D E is the value of an abnormality caused by the identifier. HE E a P A L O C Function Sets S TAT U S_N _ _ M E M O q. If the programmer wants to define its own exception identifier, you should follow the format of the standard Wi N D O W s error code, as defined in the Wi N e R R. H file. If you want to build your own anomaly code, you want to populate 4 parts of D W O R d: • The 31st and 3rd 0 bits contain severity coefficient (S e v E R Y). • The second 9th is 1 (0 represents the abnormality established by Microsoft, such as the S Tat U S_ N O _ M E M O RY) of H E A P A L O C. • The 2nd 8 is 0. • Section 2 7 to 1 6 is a device code defined by a Microsoft. • Section 1 5 to 0 is an arbitrary value to identify blocks that cause an exception.
The second parameter D w e x C e p T I o N f L A g s must be 0 or E N c o N t i n u A b L E. Essentially, this flag is used to specify whether an abnormal filter returns E X C E P Ti o n_Continue_execution to respond to whether the abnormality triggered is legal. If the exception_ noncontinuable parameter value is passed to R A I S e e x c e p t I o n, the filter can return e x C e p T I o N _ C O N t I n u e _e x e c u T i o N. Under normal circumstances, this will cause the thread to re-execute the same C P U instruction for the initial software exception. However, Microsoft has made some actions, so after calling R A I S, after the execution will continue. If you pass the E x C e p t i o n_ n o N c o n t i n u a b l e mark, you are not executed by telling the system, you can't continue to execute. This logo is used inside the operating system to convey the fatal (unrecoverable) error message. In addition, when the H e a p A L O c trigger S TAT U S_ N o _ M E M O Ry software exception, it uses E x C e p T I o N _ N o N c O N t I n u a b L E flag to tell the system, this exception cannot be continued. It means that there is no way to force allocation of memory and continue to run. If a filter ignores E x C e p T I o N _ N o N c o N t I n u A b L E and returns E x C e p T I o N _ C O N T I o n When the program is handling an exception, it is possible to raise another exception. For example, an invalid memory saving may occur in a f i n a l y block, an exception filter, or an exception handler. When this happens, the system is stack anomalies. Recalling G E TE E X C E P Ti N I N F O R M A T I O n Function. This function returns the address of the EXCETION_ POINTERS structure. E x C e p T I o N R EX C O R D member points to an Exception_ R E C O R D structure, which includes another E x C E P Ti O N R E C O R D. This member is a pointer to the additional E X C E P T I O N _R E C O R D, and this structure contains information about the previously triggered exception. Usually only one exception is processed at a time, and E x C e p T I o N r e c O R member is N u L L. However, if another exception is processed, the first Exception _ Record structure contains information about the recent rapid exception, and this Exception _ Record structure E Xception R ecord member points to the previous exception Exception _R ecord structure.
If the increased exception is not completely processed, you can continue to search for the linked list of this E x C e P T I O N _ R E C O R D structure to determine how exceptions are handled. The third parameter n n n n u m b E R O f A RG U m e n t s and the fourth parameter P A RG U m e n T s is used to transmit additional information about the extension. Generally, an additional parameter is not required, simply transmit N u L1 on the P a RG U m e n t-parameter, in which case R A I S e e x c e P T I O n function ignores N n u m b E R O F A RG U m e n t-parameter. If the additional parameter is required, the N n u m b e R O f A RG U m e n t-parameter must specify the number of elements in the U L O N g _ P R argument pointed by the P A RG U m e n t S. This number cannot exceed E X C E P T I O N _ m A x I m U m _ PA R A M E TEE R S, Exception_ Maximum_Parameters are defined in Wi N N T. h in 1 5. During this exception, the exception filter can be enabled by N u m b E R P A R A M e t eR m b E R P A R A M e t e R s and E X C E R P A R A M E TET ER E R M A T I O F A RG U M E N T S and the P A RG Um E N T S.
You may want to produce your own software exception in your own program because of some reason. For example, you may want to send a notification message to the system's event log. Whenever a function in the program finds a problem, you can call R A I S E E X C E P T I O N and let some exception handlers trace the tree to see a specific exception, or write an exception to the log or pop up a message box. You may also want to build software abnormalities to convey information within the program.