Strict Standards: Non-static method StringParser_Node::destroyNode() should not be called statically, assuming $this from incompatible context in /www/htdocs/w008ab83/ad/stringparser_bbcode/src/stringparser.class.php on line 356
AUDACIA Software - External Exception EEFFACE
AUDACIA Software

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



  1. Woher kommt diese Fehlermeldung?
  2. Was passiert, wenn die Exception geworfen wurde?
  3. Warum kann Delphi C++-Exceptions nicht behandeln?
  4. Delphi-Exceptions in C++Builder
  5. C++-Exceptions in Delphi - Der Ansatz von Early Ehlinger
  6. C++-Exceptions in Delphi - mein Ansatz
  7. Advanced Exception Dispatching
  8. Und warum hat Borland das nicht von Anfang an so implementiert?
  9. Windows Error Reporting
  10. Zusammenfassung
  11. Referenzen
  12. 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:

External Exception EEFFACE

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 Toolsjeder

Delphi-Exceptions in C++Builder


Dieses kleine Beispiel erzeugt eine hilfreiche Debug-Ausgabe, und der Handler in der VCL-Window-Prozedur zeigt diese Meldung:

You are one off!
        throw new ERangeError ("You are one off!");

C++-Exceptions in Delphi - Der Ansatz von Early Ehlinger

TranslateStandardExceptionsinvalid vector<> subscript
// 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++ 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
  • 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 Softwarenichtinvalid vector<> subscript

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):

invalid vector<> subscript - reloadedJclDebug, madExcept und EurekaLog
void __fastcall TFrmMain::BtnUserNotificationExceptionClick(TObject* Sender)
{
    throw user_notification_exception ("Don't do that!");
}

Don't do that!


Und warum hat Borland das nicht von Anfang an so implementiert?


Disclaimer:


Addendum vom 28.02.2010:

Windows Error Reporting

Windows 7-Logo-Programm
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.
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 BeispielprogrammExcTestaktuellste Version

Zusammenfassung

Software

Referenzen


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

Neuer Eintrag:

Name:
E-Mail:
Website:
Datum:
Anzahl der Zeichen in Ihrem Namen:
Text: