1. C exception and mixed use of Visual C SEH
I introduced Structured Exception Handling (SEH) in Part 2. I said at that time, SEH was proprietary on the compiler on Window and its platform. It is not defined in the ISO C standard, using a program that uses it will not be transplanted across the media. Because I have focused on standard compatibility and portability, I am very interested in EXCEPTION HANDING (EH) that maps Windows proprietary SEH maps to ISO standard C .
At the same time, I am not an expert in SEH. Most of the understanding of it comes from the previous research in this column. When I consider mixing SEH and EH, I guess the solution should be difficult and not obvious. This is why it spent two weeks: I anticipate that there is an additional time to study and test.
Very happy, I am completely wrong. I don't know that the Visual C runtime library has directly supported most of what I want. You don't have to create a new method, I can show what you have supported by Visual C , and how to transform the things you need. Based on this purpose, I will study four different versions of the same example.
1.1 Version 1: Define a conversion function
Bundles SEH and EH methods are divided into two steps:
l A user-defined conversion function to capture the exception of SEH and map it into C exception.
l A Visual C runtime library function to install this conversion function
User-defined conversion functions must have the following form:
Void my_translator (unsigned code, exception_pointers * info);
The conversion function accepts a SEH exception (defined by a given exception Code and INFO). Then throw a C abnormality to make this incoming SEH abnormality to the c exception to the rumor. This C exception will appear on the original SEH exception occurred and spread outwards.
This mechanism is very icon std :: set_terminate () and std :: set_unexpected (). To install the conversion function, call the Visual C library function_SET_SE_TRANSLATOR (). This function is declared in the header file EH.H:
Typedef void (* _SE_TRANSLATOR_FUNCTION) (unsigned, exception_pointers *);
_SE_TRANSLATOR_FUNCTION _SET_SE_TRANSLATOR (_SE_TRANSLATOR_FUNCTION);
It accepts a pointer to the new conversion function, returns the last installed pointer. Once a conversion function is installed, the previous one is lost; only one conversion function is valid at any time. (In multi-threaded programs, each thread has a separate conversion function.)
If the conversion function is not installed, the first call _SET_SE_TRANSLATOR () return value may be (or may not) NULL. That is, the function is called by the pointer returned without zeal. Very interesting, if the return value is null, and you will generate a SEH exception through this NULL call, and enter the conversion function you just installed.
A simple example:
#include
Using namespace std;
int main ()
{
Try
{
* (int *) 0 = 0; // generate structured exception
}
Catch (unsigned exception) {
Cout << "CAUGHT C Exception" << Hex << Exception << Endl;
}
Return 0;
}
If you run it, this console program will lead to such a Windows Messagebox:
It is due to an unusually captured SEH exception transfer to the outside of the program.
Now, add an exception conversion function and set the Visual C Runtime to use this conversion function:
#include
Using namespace std;
#include "windows.h"
Static void my_translator (unsigned code, exception_pointers *)
{
Throw code;
}
int main ()
{
_SET_SE_TRANSLATOR (My_Translator);
Try
{
* (int *) 0 = 0; // generate structured exception
}
Catch (unsigned Exception)
{
Cout << "CAUGHT C Exception" << Hex << Exception << Endl;
}
Return 0;
}
Run the program again. It will now be seen:
CAUGHT C EXCEPTION C0000005
MY_TRANSLATOR () intercepted SEH exception and converted to C exception, which is unsigned, the content is SEH exception code (C0000005h in this example, it is an illegal read error). Because this C exception occurs in the original SEH exception generating point, it is said in the TRY block, so it is captured by the TRY block's exception handler.
1.2 Version 2: Define a conversion object
The above example is very simple, converts each SEH to a unsigned value. In fact, you may need a more complex anomaly:
#include
Using namespace std;
/ / # include "windows.h"
#include "structured_exception.h"
/ * static void my_translator (unsigned code, exception_points *)
{
Throw code;
} * /
int main ()
{
// _ set_se_translator (my_translator);
Structured_exception :: install ();
Try
{
* (int *) 0 = 0; // generate structured exception
}
Catch (structured_exception constatabase)
{
COUT << "CAUGHT C Exception" << Hex << Exception.What ()
<< "thrown from" << exception.where () << endl;
}
Return 0;
}
This example throws a user-defined type (structured_exception) C exception. In order to make this example more practical, it is more convenient to read, I put Structured_Exception's declaration in the header file STRUCTED_EXCEPTION.H: #IF! Defined Inc_STRUCTUITED_EXCEPTION_
#define inc_structured_exception_
#include "windows.h"
Class Structured_exception
{
PUBLIC:
Structured_exception (Exception_Pointers Const &);
Static void install () through ();
Unsigned what () const throw ();
Void const * where () const throw ();
Private:
Void const * address_;
Unsigned code_;
}
#endif //! defined inc_structured_exception_
It implements the documentation:
#include "structured_exception.h"
#include "eh.h"
//
// ::
//
Static void my_translator (unsigned, exception_pointers * info)
{
Throw structured_exception (* info);
}
//
// structured_exception ::
//
Structured_exception :: structured_exception
(Exception_Pointers Const & Info) throw ()
{
EXCEPTION_RECORD Const & Exception = * (Info.exceptionRecord);
Address_ = Exception.ExceptionAddress;
Code_ = exception.exceptioncode;
}
Void structured_exception :: install () throw ()
{
_SET_SE_TRANSLATOR (My_Translator);
}
Unsigned structured_exception :: what () const throw ()
{
Return Code_;
}
Void const * structured_exception :: Where () const throw ()
{
Return Address_;
}
The meaning of these functions is:
l my_translator () is an exception conversion function. I move it from the main file. So, the main file no longer needs to contain Windows.h.
l Install () Set the global conversion function of the runner library to my_translator ().
l structured_exception constructor receives and parses the information of SEH exceptions.
l What () Returns the exception code of SEH.
l Where () Returns the location where the SEH exception occurs. Note that the return type of where () is Void const *, although the C standard does not agree to convert the code address to a Void pointer. I just repeated MICORSOFT usage because the Visual C runtime set the address of the address of the SEH exception Exception_Record.
Compile and link these three files. The result is: caught C Exception C0000005 thrown from 0040181D
(The code address value may vary on your system.)
1.3 Version 3: Imitation C Standard Runture
In My_Translator (), all SEH exception maps to the same STRUCTURED_EXCEPTION type. This makes an exception easily captured because they matches our unique exception handlers:
Catch (structured_exception constatabase)
Although the exception is captured, we have no way to know the type of abnormality in advance. The only thing that can do is the runtime query, call this exception's What () member:
Catch (structured_exception constatabase)
{
Switch (Exception.What ())
{
Case exception_access_violation:
// ...
CASE exception_int_divide_by_zero:
// ...
Case exception_stack_overflow:
// ...
// ...
}
Such queries require information in Windows.h to know the initial SEH exception code. Such a demand violates the abstract principle of Structured_exception. In addition, Switch statements often violate the principles of polymorphism. From the perspective of user code, you should usually use inheritance and templates to implement it.
The C standard runtime provides some guidance in this regard. As I have ticked in part3, the header file
According to this spirit, Standard_Exception's declaration is:
#if! defined inc_structured_exception_
#define inc_structured_exception_
#include "eh.h"
#include "windows.h"
Class Structured_exception
{
PUBLIC:
Structured_exception (Exception_Pointers Const &);
Static void install () through ();
Virtual char const * what () const throw ();
Void const * where () const throw ();
Private:
Void const * address_;
// unsigned code_;
}
Class Access_violation: public structured_exception
{
PUBLIC:
Access_violation (Exception_Pointers Const &);
Virtual char const * what () const throw ();
}
Class Divide_by_zero: public structured_exception
{
PUBLIC:
Divide_by_zero (Exception_Pointers Const &) (); Virtual Char const * what () const throw ();
}
#endif //! defined inc_structured_exception_
The implementation is:
#include
Using namespace std;
#include "structured_exception.h"
#include "windows.h"
//
// ::
//
Static void my_translator (unsigned code, exception_points * info)
{
Switch (Code)
{
Case exception_access_violation:
Throw Access_violation (* info);
Break;
CASE exception_int_divide_by_zero:
CASE exception_flt_divide_by_zero:
Throw Divide_BY_ZERO (* INFO);
Break;
DEFAULT:
Throw structured_exception (* info);
Break;
}
}
//
// structured_exception ::
//
Structured_exception :: structured_exception
(Exception_Pointers Const & Info) throw ()
{
EXCEPTION_RECORD Const & Exception = * (Info.exceptionRecord);
Address_ = Exception.ExceptionAddress;
// code_ = exception.exceptioncode;
}
Void structured_exception :: install () throw ()
{
_SET_SE_TRANSLATOR (My_Translator);
}
Char const * structured_exception :: What () const throw ()
{
Return "unspecified structured";
}
Void const * structured_exception :: Where () const throw ()
{
Return Address_;
}
//
// Access_violation ::
//
Access_violation :: Access_violation
(Exception_Pointers Const & Info) throw ()
: Structured_Exception (Info)
{
}
Char const * access_violation :: what () const throw ()
{
Return "Access Violation";
}
//
// Divide_by_zero ::
//
Divide_by_zero :: Divide_By_Zero
(Exception_Pointers Const & Info) throw ()
: Structured_Exception (Info)
{
}
Char const * Divide_by_zero :: what () const throw ()
{
Return "Divide By Zero";
}
note:
l Switch statements in the user's exception handler, now moved to my_translator (). It is no longer map all SEH anomaly into a single value (such as Version 1) or a single type of object (Version 2), and now MY_TRANSLATOR () maps them into multiple types of objects (depending on the actual environment at runtime) . l structured_exception has become a base class. I didn't let it become a pure deficiency, which is the guidance from the C standard runtime (std :: exception is an entity class).
l I didn't define any parsive function, because the compiler implied the destructor provided by these simple classes. If I define a destructor, they will need to be defined as Virtual.
l what () Now returns a user-friendly text that replaces the original SEH exception code.
L Because I no longer test and display these code, I removed the data member code_. This makes the StructureD_Exception object size decrease. (Don't be too happy: Save space is offset by new VPTR pointers because there is a virtual function.)
L Because the template is better, you should give up this inheritance mode. I will leave it to you as an exercise.
Try a new solution and change the main file to:
#include
Using namespace std;
#include "structured_exception.h"
int main ()
{
Structured_exception :: install ();
//
// Discriminate Exception by Dynamic Type
//
Try
{
* (int *) 0 = 0; // generate structured exception
}
Catch (structured_exception constatabase)
{
Cout << "caught" << exception.What () << endl;
}
//
// Discriminate Exception by Static Type
//
Try
{
Static volatile INT i = 0;
i = 1 / i; // generate structured exception
}
Catch (Access_violation Const &)
{
Cout << "caught access violation" << endl;
}
Catch (Divide_By_Zero Const &)
{
COUT << "caught divide by zero" << endl;
}
Catch (structured_exception constatabase
{
Cout << "caught unspecified structure exception" << ENDL;
}
Return 0;
}
Run again, the result is:
Caught Access Viology
Caught Divide by Zero
1.4 Version 4: Matching the C Standard Runture
All of our STANDARD_EXCEPTION inheritance provides public members
Virtual char const * what () const; to identify an abnormal dynamic type. I am not just selected. The name of the STD :: Exception inherited in all C standard runtime provides the same public member. And what () is the unique polymorphic function of each inheritance class.
You may have noticed:
#include
Class Structured_exception: Public Std :: EXCEPTION
{
PUBLIC:
Structured_exception (Exception_Pointers Const & info) throw ();
Static void install () through ();
Virtual char const * what () const throw ();
Void const * where () const throw ();
Private:
Void const * address_;
}
Because Structured_Exception is now a std: Exception, we can use an exception handler to capture this astrorse:
Catch (std :: exception constings)
And use the same polymorphic function to get an exception type:
Catch (std :: exception constings)
{
Cout << "caught" << exception.what ();
}
In this case, SEH abnormally can be consistent with the inherent behavior of standard C . At the same time, we can still treat structured_exceptions and visit its special members:
Catch (structured_exception constatabase)
{
Cout << "Caught Structured Exception from" << exception.where ();
}
Of course, if you want to give up the class members who have not appeared in the std :: Exception inheritance system, such as where (), you can completely do not use the base class structured_exception, but directly from Std :: Exception to the Access_violaion class. For example: a Divide-by-Zero anomaly represents a program value domain control error, which means a logical error. You really want to get from std :: logic_error even std :: out_of_range derived devide_by_zero classes.
I suggest you look at C Standard Subclause 19.1 ("Exception Classes") to better understand the exception inheritance system of C standard runtime, and how to better melt your custom abnormally in this inheritance system.
1.5 total end
(slightly)