|
External Exception EEFFACE
Warum Delphi C++-Exceptions nicht behandeln kann - und wie sich das ändern läßt.
Moritz Beutel, 02.10.2008, 28.02.2010
- Woher kommt diese Fehlermeldung?
- Was passiert, wenn die Exception geworfen wurde?
- Warum kann Delphi C++-Exceptions nicht behandeln?
- Delphi-Exceptions in C++Builder
- C++-Exceptions in Delphi - Der Ansatz von Early Ehlinger
- C++-Exceptions in Delphi - mein Ansatz
- Advanced Exception Dispatching
- Und warum hat Borland das nicht von Anfang an so implementiert?
- Windows Error Reporting
- Zusammenfassung
- Referenzen
- Kommentare
#include <vector>
// ...
void __fastcall TFrmMain::BtnThrowExceptionClick(TObject* Sender)
{
std::vector <String> myStrings;
myStrings.push_back ("Some string");
Caption = myStrings.at (1);
}
Wird der Button angeklickt, so erscheint diese Meldung:
Woher kommt diese Fehlermeldung?// vector, l.640ff
const_reference at(size_type _Pos) const
{ // subscript nonmutable sequence with checking
if (size() <= _Pos)
_Xran();
return (*(begin() + _Pos));
}
Nun springen wir in _Xran() hinein und treffen auf den Verursacher:
// vector, l. 1231ff
void _Xran() const
{ // report an out_of_range error
_THROW(out_of_range, "invalid vector<T> subscript");
}
Was passiert, wenn die Exception geworfen wurde?RaiseException()// 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;
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 }
Warum kann Delphi C++-Exceptions nicht behandeln?OWL, so einen Header-Converter zu schreiben, und es gibt auch ToolsjederDelphi-Exceptions in C++Builder
Dieses kleine Beispiel erzeugt eine hilfreiche Debug-Ausgabe, und der Handler in der VCL-Window-Prozedur zeigt diese Meldung:
throw new ERangeError ("You are one off!");
C++-Exceptions in Delphi - Der Ansatz von Early EhlingerTranslateStandardExceptions// 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
- Beim Start der Anwendung wird install_exception_object_handler() aufgerufen und installiert einen Exception-Filter in Delphis Exception-System.
- Wird der Filter vom Exception-System in der Delphi-RTL aufgerufen und ist der Exception-Code 0x0EEFFACE, so wird get_std_exception aufgerufen, um den Objektzeiger nach std::exception zu casten.
Besonderer Beachtung bedarf dieser Abschnitt:
__try
{
p_std_exception = dynamic_cast< std::exception const* >( p_exception );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{ } "IsBadXxxPtr should really be called CrashProgramRandomly" und Larry Ostermans "Should I check the parameters to my function?"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 |
- Das Exception-Objekt wird nicht freigegeben; es bleibt ein Speicherleck.
- "Homework assignment about window subclassing" von Raymond Chen.)
C++-Exceptions in Delphi - mein Ansatz- ECppException::TypeName resultiert in typeid (<C++-Exception-Objekt>).name().
- ECppException::CppExceptionObject
- Mit ECppException::IsCppClass kann festgestellt werden, ob es sich bei dem Exception-Objekt um eine Klasse handelt, und mit ECppException::AsCppClass() kann der Objektzeiger zu einer beliebigen Basisklasse (auch zu Interfaces, von denen der Objekttyp ableitet) gecastet werden. (Das Klassentemplate CppExceptionWrapper vereinfacht das Dispatching auf diesem Wege etwas.)
- ECppStdException::StdException
Dies ist der Interface-Abschnitt von SystemCppException:
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 */'*)
Das Unit kann im Softwarenicht
Der Exception-Typ wird nur im Debug-Build der Exception-Meldung vorangestellt; im Release-Build erscheint er nicht in der Meldung.
Advanced Exception Dispatching
Die korrekte Darstellung von C++-Exception-Meldungen ist aber nicht alles; SystemCppException kann mehr. Benutzt man beispielsweise das TApplicationEvents::OnExceptionder "-xp"-Switch aka "Positionsinformation"
Das Beispiel mit std::vector<>, das ich zu Anfang zeigte, resultiert nun in der folgenden Fehlermeldung (im Release-Build):
JclDebug, madExcept und EurekaLogvoid __fastcall TFrmMain::BtnUserNotificationExceptionClick(TObject* Sender)
{
throw user_notification_exception ("Don't do that!");
}
Und warum hat Borland das nicht von Anfang an so implementiert?
Disclaimer:
Addendum vom 28.02.2010:
Windows Error ReportingWindows 7-Logo-ProgrammMicrosoft: 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. in den Embarcadero-Newsgroups auf, und dort wurde auf die Variable System::JITEnable verwiesen, die das Verhalten eines Delphi- oder C++Builder-Programmes bei unbehandelten Exceptions konfigurierbar macht. Um die Auswirkungen verschiedener Kombinationen von JITEnable-Werten und SystemCppException zu illustrieren, habe ich ein kleines Beispielprogrammaktuellste VersionZusammenfassungSoftwareReferenzen
[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] Der Quelltext der Runtime-Libraries von Delphi und C++Builder (in C++Builder enthalten)
Kommentare
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? |
|