Translate C++ Exceptions to VCL Exceptions

Index

A Library for Converting C++ Style Exceptions Into VCL Style Exceptions
Copyright (c) 2003, 2004 Early Ehlinger

Download

TranslateStandardExceptions.zip

Tested Platforms

(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.)

News

  • 2003-03-05 - I've downloaded and installed BCB6 Update 4. It appears that Borland thankfully did not do anything to break this code. If I discover any bugs, they will be fixed for Update 4. I do not and cannot support older versions or older update packs. Wait, what am I talking about? This is totally unsupported code and is distributed with a "use at your own risk" license anyways. Allow me to rephrase: I do not support this code at all. If you find a bug, I'd love to hear about it, but I make absolutely no promise that I'll ever fix it (I'll probably try if I have some free time, but again, no promises).
  • Overview

    Get rid of "External Exception EEFFACE" errors, once and for all!

    License

    Gnu Lesser General Public License, v2.1 (See TranslateStandardExceptions.h)

    Installation

    Somewhere on your include path, unzip TranslateStandardExceptions.zip. Two files should be placed into a folder called ResBCB, TranslateStandardExceptions.cpp, and TranslateStandardExceptions.h.

    Usage

    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.

    Introduction

    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.

    The 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!

    TranslateStandardExceptions

    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++ ExceptionVCL 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
    

    Warnings

    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.

    Change Log