A Library for Converting C++ Style Exceptions Into VCL Style Exceptions
Copyright (c) 2003, 2004 Early Ehlinger
(Note: All testing has been done on Windows 2000 SP3. I do not believe that anything in this code is specific to Windows 2000, but I haven't confirmed otherwise.)
Get rid of "External Exception EEFFACE" errors, once and for all!
Gnu Lesser General Public License, v2.1 (See TranslateStandardExceptions.h)
Somewhere on your include path, unzip TranslateStandardExceptions.zip. Two files should be placed into a folder called ResBCB, TranslateStandardExceptions.cpp, and TranslateStandardExceptions.h.
In the main application file (the one that the IDE names <Project>.cpp, where <Project> is the name of your project, simply
#include <ResBCB/TranslateStandardExceptions.cpp>I originally tried to make this into a .lib you could link in, but that had a tendency to crash things for unknown reasons. If you have more success along those lines, please contact Early Ehlinger at early@ehlinger.com.
This library module was originally developed for Borland C++ Builder 6.0 Update 2 applications with the goal of translating C++-style exceptions into Delphi/VCL/CLX style exceptions with minimal redundant code.
If you allow a non-VCL/CLX exception to leak from an event handler in your code back into the VCL/CLX framework, the framework will display a rather useless message, something along the lines of "External Exception EEFFACE". Essentially, this message means that a C++ exception has leaked into the framework and the framework doesn't know what to do.
In our experience, a message is typically the right thing to do, just not the message provided. The typical approaches to getting a useful message are:
1. replace C++ exception types with VCL/CLX exception types.
2. wrap every event handler in a try/catch and convert the exceptions or display message boxes.
Option 1 can be considerably painful if you are using a C++ library that needs to be portable to other environments.
Option 2 is quite error prone and requires extra try-blocks throughout your program, something which is entirely unnecessary given that the VCL framework already has a global exception filter.
Windows supports a language-independent form of exceptions called "Structured Exceptions." Essentially, when an application throws a structured exception, it builds a record and generates a software interrupt. This initiates the stack unwinding process. In C and C++ (both Microsoft and Borland), this results in __except and __finally blocks getting executed during the unwind. Hardware exceptions, such as division by zero or access violations, are handled in exactly the same way.
Windows also supports a mechanism for filtering exceptions and translating them. It's quite involved, and not for the faint of heart. Fortunately, the VCL/CLX framework installs an exception filter that we can leverage. The purpose of this filter is to translate hardware exceptions into Delphi exceptions. Deep inside of this code, in an assembly function called _HandleOnException located in system.pas, the framework makes a call to a function pointer, ExceptObjProc. The purpose of this function is to examine the record generated at the time that a structured exception was raised, and allocate a VCL/CLX style exception. Although this function pointer is undocumented, Borland did have the presence of mind to make this function pointer a variable, and a public variable at that. In other words, we can replace that function pointer with our own!
This module uses the #pragma startup/#pragma exit facilities to install and remove a replacement ExceptObjProc, GetCppExceptionObject. This function examines the Structured Exception Record and checks to see if it is a C++ Exception, as indicated by an exception code of 0xEEFFACE. If it isn't a C++ Exception, GetCppExceptionObject simply passes the exception down to the default ExceptObjProc, GetExceptionObject, which is defined in SysUtils.pas.
If, on the other hand, the Structured Exception Record /does/ represent a C++ exception, GetCppExceptionObject inspects the object that was thrown and allocates a VCL-style exception of an appropriate type. Here is the map that it uses:
| C++ Exception | VCL Exception |
|---|---|
| std::logic_error | CppStdLogicError |
| std::domain_error | CppStdDomainError |
| std::invalid_argument | CppStdInvalidArgument |
| std::length_error | CppStdLengthError |
| std::out_of_range | CppStdOutOfRange |
| std::runtime_error | CppStdRuntimeError |
| std::range_error | CppStdRangeError |
| std::overflow_error | CppStdOverflowError |
| any other exception derived from std::exception | CppStdException |
| any c++ exception not derived from std::exception | CppException |
The VCL-style exception types above form a heirarchy roughly resembling the same heirarchy employed on the C++ side of things. (Recall that in C++ you can throw *anything*, including things like int, so there is no single base exception class on the C++ side). The heirarchy looks like this:
Exception
|
+- (Various VCL Exception classes)
|
+- CppException
|
+-- CppStdException
|
+- CppStdLogicError
+- CppStdDomainError
+- CppStdInvalidArgument
+- CppStdLengthError
+- CppStdOutOfRange
+- CppStdRuntimeError
+- CppStdRangeError
+- CppStdOverflowError
This library makes use of some undocumented aspects of Borland C++ Builder. It is not generally safe to assume that Borland will not change these aspects. In fact, it is probably safer to assume that they will. In other words, this library will likely only work on the version of BCB where it was most recently tested. If you have another version, you need to spend some time learning assembly and tracing through the runtime library source code to verify that this code works on your version. You will almost assuredly need to make changes to the code; the technique will probably be similar however. The bottom line is that if you're using anything other than the version of BCB listed above then you will have to modify this code and produce a derivative work.