|
External Exception EEFFACE
Why Delphi cannot catch C++ exceptions and how to change it.
Moritz Beutel, October 2nd, 2008, February 28th, 2010
- Introduction
- Where does the message come from?
- What is happening after that exception is thrown?
- Why is Delphi unable to handle C++ exceptions?
- Delphi exceptions in C++Builder
- C++ exceptions in Delphi - Early Ehlinger's approach
- C++ exceptions in Delphi - my approach
- Advanced Exception Dispatching
- So why did Borland not implement this in the first place?
- Windows Error Reporting
- Summary
- References
- Comments
Introduction
Practically every C++Builder programmer - and, unfortunately, many users of programs written in C++Builder - is aware of the "External Exception EEFFACE" message box. This message pops up if a C++ exception is not caught explicitly and leaks into the VCL.
To reproduce this error in a real-world-like scenario, create a new VCL Forms project and insert some code like this:
#include <vector>
// ...
void __fastcall TFrmMain::BtnThrowExceptionClick(TObject* Sender)
{
std::vector <String> myStrings;
myStrings.push_back ("Some string");
Caption = myStrings.at (1);
}
Now build and run the program and click the button. You will receive this message:
After closing the message box, the program continues to run.
Where does the message come from?
The message is, as mentioned above, originated by a C++ exception. The error in the above code should be obvious; to see what is happening, we use the debugger to step into the call of std::vector<>::at(). We end up here:
// vector, l.640ff
const_reference at(size_type _Pos) const
{ // subscript nonmutable sequence with checking
if (size() <= _Pos)
_Xran();
return (*(begin() + _Pos));
}
Stepping into _Xran() reveals the actual throw statement:
// vector, l. 1231ff
void _Xran() const
{ // report an out_of_range error
_THROW(out_of_range, "invalid vector<T> subscript");
}
std::vector<>::at() throws an exception of the type std::out_of_range if we pass an invalid index. The error message is pretty clear and should give a clue for locating the cause of the problem - if only our program displayed it!
What is happening after that exception is thrown?
This can easily be monitored in the disassembly view. The compiler generates a call to the RTL function _ThrowExceptionLDTC() (you can find the implementation of that function and most of the exception handling stuff in $(BDS)\source\cpprtl\Source\except\xx.cpp). This function calls tossAnException(), which itself finally calls the RaiseException() function. RaiseException() searches for an exception handler, and the next handler that can be found is this one:
// Controls.pas, l. 9059ff
procedure TWinControl.MainWndProc(var Message: TMessage);
begin
try
try
WindowProc(Message);
finally
FreeDeviceContexts;
FreeMemoryContexts;
end;
except
Application.HandleException(Self);
end;
end;
As you can recognize, a general exception handler resides in the main window procedure. It catches everything, be it a Delphi exception (which is nicely dispatched and shown by Application.HandleException()), an OS exception (they are translated to Delphi exceptions) or something else, e.g. a C++ exception. For Exceptions the handler is not able to handle properly, it generates a message which contains the exception code. A few exception codes defined in System.pas are:
const { copied from xx.h }
cContinuable = 0;
cNonContinuable = 1;
cUnwinding = 2;
cUnwindingForExit = 4;
cUnwindInProgress = cUnwinding or cUnwindingForExit;
cDelphiException = $0EEDFADE;
cDelphiReRaise = $0EEDFADF;
cDelphiExcept = $0EEDFAE0;
cDelphiFinally = $0EEDFAE1;
cDelphiTerminate = $0EEDFAE2;
cDelphiUnhandled = $0EEDFAE3;
cNonDelphiException = $0EEDFAE4;
cDelphiExitFinally = $0EEDFAE5;
cCppException = $0EEFFACE; { used by BCB }
0x0EEFFACE is the exception code passed to RaiseException() in the C++ RTL, and this is what the "External Exception" message tells us.
Why is Delphi unable to handle C++ exceptions?
This is substantiated in the way Delphi and C++ work together. In the early 90s, Borland had a C++ GUI library called OWL. As Borland C++/OWL continued to lose market share against Visual C++/MFC, it was eventually decided to deprecate OWL and to adapt Delphi's highly successful VCL framework for C++. (In fact, it is not only possible but also very likely that this has been decided while the VCL was being designed; I don't know.) From this decision followed the requirement of an interface-level compatibility between Pascal and C++. While the unit interface concept of Borland's Pascal is very clear and distinct, this cannot be said for the C/C++ equivalent, header files. To enable Pascal to consume C++ header files, the Pascal compiler needed to be able to resolve all kind of dependencies to other header files and preprocessor macros, it required a mechanism to detect the correlation of header files and source modules (unlike Pascal, C++ allows to split the implementation of the interface defined in a header file across different modules - or vice versa), it would need to know about all C++ language features such as C++ templates, it even would need the capability to resolve template metaprograms (although that has most likely not been on the radar back in 1995 ;) ). Actually, it is virtually impossible to automatically generate a Delphi interface unit even for a plain C header. Although there have been several approaches to write a header converter, and some parts of this task can be simplified by helper utilities, all those rudiments are way too far from a complete solution.
Probably due to the sheer difficulty of this approach, Borland decided to go the other way around. When C++Builder was first released in 1997, the supplied Delphi compiler was able to generate C++ header files that matched the interface section of Delphi units. To make this possible, Borland's C++ dialect had to be extended by the language capabilities of Delphi which affected the interface level, most notably the rooted object type system, the RTTI (__classid, __published), some language constructs (__property, __closure), new calling conventions (__fastcall) and a few #pragma directives. As a result, the interface-level language capabilities of Delphi can be considered a discrete subset of the interface-level capabilities of Borland's C++ implementation.
(Note that this is, of course, an idealistic statement. Certain cases require explicit workarounds using Delphi compiler directives such as $HPPEMIT or $EXTERNALSYM. Also, some less widely used language features of Delphi, e.g. metaclass polymorphism/virtual constructors or, more recently, class helpers, cannot be used direcly in C++ code. Apart from class helpers which were primarily designed to make Delphi's type system fit into the .NET type system when Delphi for .NET was created, CodeGear is working on getting those discrepancies resolved. C++Builder 2009, for example, introduces the __classmethod keyword which makes possible to declare and override static virtual functions in C++.)
As a direct consequence, C++Builder is aware of Delphi's Exception type and handles it properly, while the reverse is not true. Moreover, the C++ exception mechanism is not limited to the std::exception class and its derivates. In fact, you can throw almost everything. While I don't have a clue why anyone would want to throw integers or floats, it fits the C++ philosophy well to not limit the exception mechanism to a certain base class (right, Barry :( ).
Delphi exceptions in C++Builder
In Delphi, you only throw exceptions derived from the Exception class declared in SysUtils. (Although it is technically possible to pass any object that inherits from TObject to Raise(), this is very uncommon, and it is still a sufficient constraint due to Delphi's rooted object type system. It would be trivial to discover the actual type of the object thrown.) The Exception class and all derivates can be accessed by C++Builder through the header files generated by the Delphi compiler. Therefore, you can throw and catch Delphi exceptions in C++Builder:
void __fastcall TFrmMain::BtnThrowDelphiExceptionClick(TObject* Sender)
{
try
{ // We can throw Delphi exceptions by value...
throw ERangeError ("You are one off!");
}
catch (Exception& e) // ...catch them by reference...
{
OutputDebugString ((String (e.ClassName ()) + ": " + e.Message).c_str ());
throw; // ...and re-throw them as we like.
}
}
This little example will give us a pretty informative debug output, and the VCL exception handler displays this message:
If you are familiar with the handling of Delphi-style classes in C++Builder, you might wonder why the compiler permits to construct a temporary ERangeError object on the stack as we are doing above. After all, ERangeError is derived from TObject, and every attempt to allocate TObject-derived classes on the stack usually results in an error:
[BCC32 Error] main_unit.cpp(16): E2459 VCL style classes must be constructed using operator new
There are many good reasons for this limitation, but I won't discuss them here (although I consider doing that in a future article).
Our above case is not an exception (no pun intended ;), at least from the technical point of view. If you look at the code generated for above throw statement, you will notice that the compiler does not really construct the temporary ERangeError object on the stack. Instead, it generates almost the same code as if you had written this (only the RTTI passed to _ThrowExceptionLDTC() differs):
throw new ERangeError ("You are one off!");
This compiler-level workaround is a concession to the common C++ exception semantics. In C++, exception objects are thrown by value as throwing a pointer would produce a potential leak (who frees the object? How?). Delphi exceptions are instead, by definition, thrown as pointers and freed by the handler with the TObject::Free() method.
Now try to think of what would be required to make Delphi handle C++ exceptions. The Delphi compiler would need to be acquainted with the types that can be thrown - as there is no limitation in C++, this would be all possible C++ types. So we end up having the problem we discussed above - converting C++ header files to Delphi unit interfaces is almost impossible.
C++ exceptions in Delphi - Early Ehlinger's approach
As the subtitle implies, there is a way to change that. Delphi cannot handle C++ exceptions as they are, but it can handle wrapper classes which we can generate in a custom exception filter. This approach was taken first by Early Ehlinger in his TranslateStandardExceptions unit.
When we add his unit to our project, a click on the first button results in this message:
This is want we wanted, and it might satisfy you, but I will try to explain why it shouldn't.
Let's take a look at the source code. I'll take out the relevant parts:
// Copyright (c) 2003 Early Ehlinger
// GNU Lesser General Public License, v2.1
// http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
typedef Exception* (__fastcall* ExceptObjProcType)( Sysutils::TExceptionRecord* P );
ExceptObjProcType pOldExceptObjProc = NULL;
class not_standard_exception
{
public:
virtual void foo() = 0;
};
std::exception const* get_std_exception( not_standard_exception const* p_exception )
{
std::exception const* p_std_exception = 0;
__try
{
p_std_exception = dynamic_cast< std::exception const* >( p_exception );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{ }
return p_std_exception;
}
Exception* __fastcall GetCppExceptionObject(Sysutils::TExceptionRecord* P)
{
// ...
if ( std::exception const* p_std_exception = get_std_exception( p_exception ) )
// ...
}
void install_exception_object_handler( )
{
pOldExceptObjProc = reinterpret_cast< ExceptObjProcType >( ExceptObjProc );
System::ExceptObjProc = GetCppExceptionObject;
}
#pragma startup install_exception_object_handler
void uninstall_exception_object_handler( )
{
if ( pOldExceptObjProc )
System::ExceptObjProc = pOldExceptObjProc; // System.pas
}
#pragma exit uninstall_exception_object_handler
To fully understand what this code is doing, read Early's explanation on his website or in the source code comments. Basically, this is what happens:- At startup, install_exception_object_handler() is called and installs an exception filter into Delphi's exception system.
- When the exception filter is invoked by Delphi's exception system and the exception code equals 0x0EEFFACE, get_std_exception is called to cast the pointer to std::exception*.
- get_std_exception treats the pointer as a pointer to a polymorphic object and tries to cast it to std::exception with dynamic_cast<>. If this fails, it returns 0.
- If get_std_exception succeeded, the exception filter composes an error message from the exception type and the message returned by std::exception::what(). Otherwise, it generates a more general message.
Now take a closer look at this specific part:
__try
{
p_std_exception = dynamic_cast< std::exception const* >( p_exception );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{ }
If you don't realize why this code is evil, read "IsBadXxxPtr should really be called CrashProgramRandomly" by Raymond Chen and "Should I check the parameters to my function?" by Larry Osterman.
And there are more problems which prevented me from using Early's version:- Early provides Delphi exception classes which resemble the basic standard exception types of C++:
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 |
While this is a pragmatic approach that allows for dispatching of some basic exception types defined by the standard, it leads to code redundancy and is not extensible.
- The exception object is not freed, so we still have a leak.
- Also, the unit is not of much help if your application is written in Delphi and uses packages compiled with C++Builder - the best example for this scenario being the C++Builder IDE itself. In that case, the application should, although written in Delphi, be able to handle C++ exceptions, but it is currently not possible to add units written in C++ to a Delphi application. (Adding this unit to all C++Builder packages does not solve this problem but introduces a new one as packages can be unloaded at runtime; read Raymond Chen's "Homework assignment about window subclassing" for more details on this kind of problem.)
- And finally, many C++Builder programmers are writing commercial applications and cannot use source code that is licensed under GPL or LGPL.
After so many problems coming up, I decided to find another solution.
C++ exceptions in Delphi - my approach
Early's unit gave me the idea to use the exception filter to convert C++ exceptions to Delphi exceptions. This is where I started.
To fix the Delphi application/package problem, I decided to create a Delphi unit as those can be added to both Delphi and C++Builder projects.
While debugging the call of _ThrowExceptionLDTC(), I found that the C++ compiler passes a pointer to the RTTI descriptor to the function. This pointer is stored in the exception descriptor which itself is passed to RaiseException(). This allows us to read out both the exception descriptor (which helps us to free the object correctly) and the RTTI descriptor (which allows for dynamic dispatching). All this is well documented in the RTL source code; look into $(BDS)\source\cpprtl\Source\except\xx.cpp and $(BDS)\source\cpprtl\Source\except\xxtype.cpp for more details.
And this is exactly what my unit is doing. I called it 'SystemCppException' because it is an addition to the exception handling mechanism in the System unit. To achieve this, I wrote a few Delphi functions that mimic the behavior of some exception-handling-related functions in the C++ RTL. Using them, the exception filter is able to recognize C++ exceptions, to identify their actual type, to cast them to any base class using the RTTI, and finally, to destroy and deallocate them.
To make them usable in Delphi code, the C++ exceptions are wrapped into Delphi exceptions, the ECppException and the ECppStdException classes. Their properties and methods should explain themselves, but I will mention the most useful ones:- ECppException::TypeName returns typeid (actualExceptionObject).name().
- ECppException::CppExceptionObject returns a raw pointer to the actual exception object.
- You can use the ECppException::IsCppClass property and the ECppException::AsCppClass() function to check whether the exception object is a class and to downcast it to a base class or interface, if possible. (The CppExceptionWrapper class template is helpful with dispatching such classes more easily.)
If the exception object derives from std::exception, we have a common base class which simplifies things a lot. In this case, the exception filter creates a wrapper object of type ECppStdException, which itself inherits from ECppException and provides one more property:- ECppStdException::StdException returns a pointer of type std::exception* which points to the actual exception object. Use dynamic_cast<> to dispatch it.
This is what the interface section looks like:
unit SystemCppException;
interface
uses
SysUtils;
type
PCppStdException = type Pointer; { mapped to std::exception* via $HPPEMIT }
{$EXTERNALSYM PCppStdException}
{ C++ exception of any type }
ECppException = class (Exception)
private
FTypeName: AnsiString;
FExcDesc: Pointer;
constructor CreateTypeNamed (_TypeName: PAnsiChar; ExcDesc: Pointer); overload;
function GetCppExceptionObject: Pointer;
function GetThrowLine: Integer;
function GetThrowFile: AnsiString;
public
property CppExceptionObject: Pointer read GetCppExceptionObject;
property ThrowLine: Integer read GetThrowLine;
property ThrowFile: AnsiString read GetThrowFile;
property TypeName: AnsiString read FTypeName;
function IsCppClass: Boolean;
{ ATTENTION: this function does perform downcasts only! }
function AsCppClass (CppClassName: AnsiString): Pointer;
destructor Destroy; override;
end;
{ C++ exception derived from std::exception }
ECppStdException = class (ECppException)
private
FExcObj: PCppStdException;
constructor Create (AExcObj: PCppStdException; Msg: String;
_TypeName: PAnsiChar; ExcDesc: Pointer); overload;
function GetStdException: PCppStdException;
public
{ This property returns a pointer to the wrapped exception. }
property StdException: PCppStdException read GetStdException;
destructor Destroy; override;
end;
(*$HPPEMIT '#include <typeinfo>'*)
(*$HPPEMIT '#include <exception>'*)
(*$HPPEMIT ''*)
(*$HPPEMIT 'namespace Systemcppexception'*)
(*$HPPEMIT '{'*)
(*$HPPEMIT ''*)
(*$HPPEMIT 'class DELPHICLASS ECppException;'*)
(*$HPPEMIT ''*)
(*$HPPEMIT 'template <class E>'*)
(*$HPPEMIT ' class CppExceptionWrapper'*)
(*$HPPEMIT '{'*)
(*$HPPEMIT 'private:'*)
(*$HPPEMIT ' E* internalClass;'*)
(*$HPPEMIT ' struct _safebool_t;'*)
(*$HPPEMIT ''*)
(*$HPPEMIT 'public:'*)
(*$HPPEMIT ' CppExceptionWrapper (ECppException* exception)'*)
(*$HPPEMIT ' : internalClass (static_cast <E*> (exception->AsCppClass (typeid (E).name ())))'*)
(*$HPPEMIT ' {}'*)
(*$HPPEMIT ' operator _safebool_t* (void) const'*)
(*$HPPEMIT ' { return (_safebool_t* ) internalClass; }'*)
(*$HPPEMIT ' E& operator * (void)'*)
(*$HPPEMIT ' { return *internalClass; }'*)
(*$HPPEMIT ' E* operator -> (void)'*)
(*$HPPEMIT ' { return internalClass; }'*)
(*$HPPEMIT '};'*)
(*$HPPEMIT ''*)
(*$HPPEMIT 'typedef std::exception* PCppStdException;'*)
(*$HPPEMIT ''*)
(*$HPPEMIT '} /* namespace Systemcppexception */'*)
You can download the unit in the Software section. Simply add this unit to your project, be it in Delphi or C++Builder. (But do not add it to packages which are linked with rtl*.bpl, for the reasons I explained above!)
Similar to Early's unit, this unit installs an exception filter in its initialization section and removes it in the finalization section. Just add it to your project, and the VCL exception handler will catch your exceptions and display the expected message:
The prefixed exception type name vanishes in the Release build; your customers won't see it.
Advanced Exception Dispatching
Of course, there is more value in SystemCppException than the ability to display proper exception messages. With the TApplicationEvents::OnException event, we can install a global exception dispatcher that does useful things such as sending bug reports, writing logfiles and displaying formatted and extensive messages.
Before we go on with doing that, I want to mention a nice little feature of the C++Builder compiler which is documented here: the -xp switch aka "Location information". If this option is enabled, every exception descriptor is aware of the file and the line where the exception was thrown. The exception classes declared in SystemCppException.pas use that information and provide it as read-only properties.
This example demonstrates dynamic exception dispatching and the utilization of that compiler switch:
class silent_exception : public std::runtime_error
{
public:
silent_exception (void)
: std::runtime_error ("")
{}
};
class user_notification_exception : public std::runtime_error
{
public:
user_notification_exception (const char* arg)
: std::runtime_error (arg)
{}
};
bool displayExceptionDialog (HANDLE hParentWnd, Exception* E,
bool sendReport)
{
ECppException* ce = dynamic_cast <ECppException*> (E);
String typeName;
String throwFile;
int throwLine = 0;
if (ce)
{
typeName = ce->TypeName;
throwFile = ce->ThrowFile;
throwLine = ce->ThrowLine;
}
else
typeName = E->ClassName ();
String Message = Format ("Unhandled exception of type %s:\n\n%s",
ARRAYOFCONST ((typeName, E->Message)));
DWORD questionstyle = 0;
if (!throwFile.IsEmpty ())
{
Message += Format ("\n\nThrown in: %s\nLine: %d",
ARRAYOFCONST ((throwFile, throwLine)));
}
if (sendReport)
{
questionstyle = MB_YESNO;
Message += "\n\nSend report?";
}
return (MessageBox (hParentWnd, Message.c_str (), Application->Title.c_str (),
MB_ICONERROR | questionstyle) == IDYES);
}
void sendBugReport (Exception* E)
{
// ...
}
void writeToLogFile (Exception* E)
{
// ...
}
void __fastcall TFrmMain::ApplicationEventsException(TObject* Sender,
Exception* E)
{
ECppStdException* csew;
ECppException* cew;
if ((csew = dynamic_cast <ECppStdException*> (E)) != 0) // standard C++ exception
{
std::exception* cse = csew->StdException; // Get the inner exception.
// Note that no hard cast is
// required.
// Swallow Silent Exceptions.
// (Whether Silent Exceptions are good design is debatable, but
// people use them, and it is a nice demonstration of dynamic
// exception dispatching.)
if (dynamic_cast <silent_exception*> (cse))
{
ApplicationEvents->CancelDispatch ();
return;
}
// User Notification Exceptions are not errors but caused by
// misbehavior of the user. Imagine the IDE's project manager
// which allows to rename a file by pressing F2. If the user
// enters a file name that is already used in the project,
// the project manager would throw a user notification exception,
// thus cancelling all pending renaming operations which would
// have been induced.
// Therefore, we don't want bug reports or logfile entries
// for that kind of exception.
if (dynamic_cast <user_notification_exception*> (cse))
{
Application->ShowException (E);
ApplicationEvents->CancelDispatch ();
return;
}
}
else if ((cew = dynamic_cast <ECppException*> (E)) != 0)
{ // non-standard C++ exception
}
else
{ // Delphi or OS exception
}
if (displayExceptionDialog (Handle, E, true))
sendBugReport (E);
writeToLogFile (E);
}
The std::vector<> example I showed at the beginning will now result in this error message (Release build):
In a commercial project, you would of course not want display the exception type, file name or line number in the exception message - imagine to send that information along with the automated bug report instead ;-)
(If you need more extensive bug reports including stack trace, modules list, system information etc., consider using a 3rd-party product such as JclDebug, madExcept or EurekaLog.)
Exceptions derived from user_notification_exception look like this:
void __fastcall TFrmMain::BtnUserNotificationExceptionClick(TObject* Sender)
{
throw user_notification_exception ("Don't do that!");
}
So why did Borland not implement this in the first place?
Disclaimer: This is pure speculation.
As above solution seems to integrate and to perform smoothly, the question comes up why Borland did not implement that yet. Exception handling is a challenging subject, and I am sure there are many answers to this. Some potential reasons would be:- The approach taken by me duplicates some code from the C++ RTL. Code duplication implicates maintenance difficulties, and every professional programmer wishes to avoid that, particularly in delicate areas which moreover are subject to change.
- From the language/library designer viewpoint, my approach might be questionable. Sure, the net effect is an informative message for the user instead of "External Exception EEFFACE", but consider what my unit is doing to achieve this: it wraps every unhandled C++ exception into a Delphi exception. Thus it is bypassing the clear distinction between Delphi and C++ exceptions.
I am however not sure whether the developers of Delphi and C++Builder saw a problem in this. In fact, Delphi wraps OS exceptions (such as STATUS_INTEGER_DIVIDE_BY_ZERO or STATUS_ACCESS_VIOLATION) into Delphi exceptions (EDivByZero, EAccessViolation). Although Raymond Chen says "In the Win32 programming model, exceptions are truly exceptional. As a general rule, you shouldn't try to catch them", this very feature is one of those which tend to make VCL programs more stable than other programs. The Delphi team has been proud of that feature, as the Delphi 1 launch script clearly points out:
I'm sure you've heard something like this before: "Yeah, this app is great, but boy when something goes wrong you get GP faults all over the place".
Well not with Delphi.
In fact, speaking of GP faults, let me show you what happens in Delphi.
[...]
As you can see, a GP fault occurred, and Delphi's default exception handler caught it and is showing an error dialog.
But notice that the app is still running.
- "Implementing new features was of higher priority than fixing old bugs." This would be a typical Borland answer (think C++BuilderX, Delphi 8, Delphi 2005), and I am happy that this can't be said for CodeGear anymore.
Addenda as of 28.02.2010:
Windows Error Reporting
The guidelines for the Windows 7 Client Software Logo Program require that unhandled exceptions must not be caught by the application:
Microsoft: Vendors must not hide unhandled exceptions from Windows error reporting (WER). ISVs must sign up to receive their crash data from WER.
You can do this by signing applications at Winqual. ISVs must map their applications carrying the Windows 7 logo to their company at this site and maintain these mappings while the product is supported.
The Software Logo Program came up in the Embarcadero newsgroups recently, and Allen Bauer kindly pointed to the System::JITEnable global variable which allows to configure the RTL's default exception handling behavior. To illustrate the consequences of different combinations of JITEnable and SystemCppException, I wrote a little example program which lets you experiment with the different settings:
If you want to build it yourself, be sure to get the latest revision of SystemCppException.
Summary
This document explained the origin of the "External Exception EEFFACE" message and how to get rid of it.
As I tried to point out in the last paragraph, my approach is not without problems: it wraps unhandled C++ exceptions into Delphi exceptions which might be questionable from the designer perspective, and it includes a few functions that must be kept in sync with their equivalents in the C++ RTL. Expect those implementation details to change as soon as Delphi and C++Builder go 64-Bit. But anyway, I still hope that Commodore will provide a way to handle C++ exceptions out-of-the-box.
After reading this document, you should be able to decide whether you think that my unit adds value to your project. If you decide to use it, you can find it in the Software section. (And if you like, drop me a few lines per mail or post a comment.)
References
[1] Early Ehlinger, Translate C++ Exceptions to VCL Exceptions
[2] Raymond Chen, IsBadXxxPtr should really be called CrashProgramRandomly, 27.09.2006
[3] Larry Osterman, Should I check the parameters to my function?, 18.05.2004
[4] Raymond Chen, Homework assignment about window subclassing, 10.11.2003
[5] Anders Hejlsberg, Historical Documents: Delphi 1 launch demos source code, launch script, and marketing video, 14.02.1995
[6] The Delphi and C++Builder Runtime Library source code (delivered with C++Builder)
Comments
Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /www/htdocs/w008ab83/ad/phputils/dbc_mysql.php on line 112
Name: Jarek
24.05.2010 09:11:14 |
Hello Moritz,
I've found your article very interesting and I'm going to include your unit SystemCppException in my project. But do you know if this unit co-operates well with EurekaLog?
Best wishes,
Jarek |
Name: Moritz
30.07.2010 17:12:01 |
Hello Jarek,
sorry, I don't know; I don't use EurekaLog.
But I'd be happy to hear about your experience with combining EurekaLog and SystemCppException.
Regards,
Moritz |
Name: Ahmed
26.10.2012 00:00:21 |
habe es gelöst das probelm und zwar ganz einfach den pc 2mal formatieren zu mindest war das bei mir so wenn noch fragen sind kontaktieren sie mich auf:
king_of_xbox@hotmail.com |
Name: Peter
05.09.2013 18:12:27 |
A very interesting read.
Unfortunately this is not really my cup of tea and I think I have to read it a couple more times before I even understand most of it.
As a test I included the .pas file to my project and built it (using C++ Builder 2009).
In the code I added throw(\"message\") ;
I noticed an improvement from EEFFACE to \"Unhandled C++ Exception of type \'const char *\' occurred.
I was expecting to see \"message\"
What am I missing or doing wrong ? |
Name: Neiß
01.10.2013 12:58:08 |
Hallo,
interessanter Artikel, wenn auch eine Anmerkungen erst nach ausgiebiger Lektüre der entspr. anderen Artikel verständlich werden ;-)
Einen Bug habe ich im Code gefunden..
Dieser besteht ja zu großen Teilen aus \"Nachbauten\" der entspr. Funktionen aus den C++-Moulden der RTL.
{ ... This function should basically work like locateBaseClass() in xxtype.cpp }
function _LocateCppBaseClass (BaseList: PCppBaseList; VBase: Boolean;
BaseName: PAnsiChar; var Addr: Pointer) : Boolean;
Der 2. Parameter gibt an, ob BaseList eine VBase ist oder nicht.
in CppGetBase steht aber:
Result := _LocateCppBaseClass (BaseList, False, BaseName, Obj)
or _LocateCppBaseClass (VBaseList, False, BaseName, Obj);
richtig müsste hier stehen (und damit dem original auch entsprechen):
Result := _LocateCppBaseClass (BaseList, False, BaseName, Obj)
or _LocateCppBaseClass (VBaseList, True, BaseName, Obj);
Auch wenn es sehr unwahrscheinlich ist, das eine Exception mit virtuellen Basis-Klassen geworfen wird.
Sollte es doch jemand tuen, gibt das wohl eine Access violation!
|
Name: Neiß
01.10.2013 14:43:02 |
Hallo,
interessanter Artikel, wenn auch eine Anmerkungen erst nach ausgiebiger Lektüre der entspr. anderen Artikel verständlich werden ;-)
Einen Bug habe ich im Code gefunden..
Dieser besteht ja zu großen Teilen aus \"Nachbauten\" der entspr. Funktionen aus den C++-Moulden der RTL.
{ ... This function should basically work like locateBaseClass() in xxtype.cpp }
function _LocateCppBaseClass (BaseList: PCppBaseList; VBase: Boolean;
BaseName: PAnsiChar; var Addr: Pointer) : Boolean;
Der 2. Parameter gibt an, ob BaseList eine VBase ist oder nicht.
in CppGetBase steht aber:
Result := _LocateCppBaseClass (BaseList, False, BaseName, Obj)
or _LocateCppBaseClass (VBaseList, False, BaseName, Obj);
richtig müsste hier stehen (und damit dem original auch entsprechen):
Result := _LocateCppBaseClass (BaseList, False, BaseName, Obj)
or _LocateCppBaseClass (VBaseList, True, BaseName, Obj);
Auch wenn es sehr unwahrscheinlich ist, das eine Exception mit virtuellen Basis-Klassen geworfen wird.
Sollte es doch jemand tuen, gibt das wohl eine Access violation!
|
Name: Stefano
08.01.2018 14:07:50 |
Hi Moriz,
thanks for the unit, it has been very useful for us. Unfortunately we are trying to port one of our applications (a simple one!) to Android, and SystemCppException refuses to compile, since AnsiString is not supported for Android and IOS. What can we do? |
|