wie fange ich in C++ Code die Exceptions aus C# COM?



  • Permana schrieb:

    ...
    Wenn das so ist, kann man dem Debugger beibringen, dies nicht zu tun? Die Tips, die ich dabei gefunden habe, haben bis jetzt nicht geholfen.

    Jemand hat geschrieben:
    "Ich mußte unter BBC++4.0 [I]nur[/] folgendes deaktivieren:
    Integrierte Fehlerbehandlung zulassen (o.ä.)"

    wo steht diese Option? Weiss das jemand? Ich habe Builder verison 6.



  • Permana schrieb:

    ich kann in den Logfiles meiner COM-Schnittstelle sehen, dass Exception geworfen wird.

    16.07.2010 17:22:29.238 E EXCEPTION GlobalExceptionHandler: Unerwartete Excpeption: System.InvalidCastException: Die angegebene Umwandlung ist ungültig.

    Das bedeutet, dass der C++ Builder, also vermutlich der Debugger diese Exceptions fängt und konsummiert.

    Eher nicht. Wenn du beim direkten Aufruf der COM-Schnittstelle "Der Vorgang wurde erfolgreich abgeschlossen" zurückbekommst, dann passiert die Exception gar nicht erst die COM-"Grenze"; der Fehler ist also auf der .NET-Seite.

    Du könntest mal versuchen, nicht die Late-binding-Schnittstelle zu verwenden. Ob du den Header herausgeben darfst, ist natürlich fragwürdig, aber könntest du mal (nur namentlich, inkl. Vorfahren) auflisten, welche Klassen und Interfaces alles in dem Header deklariert werden? Möglichst mit dem vom Importer generierten Kommentar darüber.

    Wenns zu viele sind, dann natürlich nur diejenigen, die mit dem, was du benutzen willst, in namentlicher Verbindung zu stehen scheinen.



  • Permana schrieb:

    Jemand hat geschrieben:
    "Ich mußte unter BBC++4.0 [I]nur[/] folgendes deaktivieren:
    Integrierte Fehlerbehandlung zulassen (o.ä.)"

    wo steht diese Option? Weiss das jemand? Ich habe Builder verison 6.

    Mannomann, die Drama geht weiter. Natürlich ist dieses Kästchen zum Hacken sehr einfach zu finden. Bei Debuuger-Optionen links unten. Leider bewirkt dies, dass man gar nicht mehr debuggen kann.

    Dann steht weiter bei "BS Exceptions" zur Auswahl, ob Exceptions von Applikation oder Debugger behandelt werden. Ist das nicht eine sehr offensichtliche Sache? Ja, doch. Bei unserer Konfiguration ist "Anwendungsprogramm" gewählt. Nur das hilft trotzdem nicht. Exceptions gehen mir verloren. 😡

    Ich setze Breakpoints an den Zeilen von utilcls_oleexc.cpp und der Debugger lehnt sie alle als ungültig ab. Also habe ich die Lösung von aucidia eigentlich noch gar nicht vollständig realisiert. 👎



  • audacia schrieb:

    ...dann passiert die Exception gar nicht erst die COM-"Grenze"; der Fehler ist also auf der .NET-Seite.

    Du könntest mal versuchen, nicht die Late-binding-Schnittstelle zu verwenden.

    ...(nur namentlich, inkl. Vorfahren) auflisten, welche Klassen und Interfaces alles in dem Header deklariert werden? Möglichst mit dem vom Importer generierten Kommentar darüber....

    Vielen Dank für Deine Aufmerksamkeit. Ich werde da einen Text vorbereiten.

    Was Early-Binding angeht, muss ich noch ein bischen studieren. :p



  • Sorry, ich muss etwas korrigieren. Ich bin übermüde und habe das Logfile zu früh aufgemacht. Die weiteren Versuche zeigen, dass die COM-Schnittstelle doch keine Exception verzeichnet. D.h. nicht mit unserer Applikation.

    Im Falle von Demo-App doch.

    Und beide Codes sind in C++.

    Da muss ich vermutlich mich ernsthaft um Late und Early Bindung kümmern.



  • audacia schrieb:

    Ob du den Header herausgeben darfst, ist natürlich fragwürdig, aber könntest du mal (nur namentlich, inkl. Vorfahren) auflisten, welche Klassen und Interfaces alles in dem Header deklariert werden? Möglichst mit dem vom Importer generierten Kommentar darüber.

    Wenns zu viele sind, dann natürlich nur diejenigen, die mit dem, was du benutzen willst, in namentlicher Verbindung zu stehen scheinen.

    Hi,

    ich freue mich weiterhin auf Feedback und habe nun das File in reduzierter Form vorbereitet. Bitte schön:

    #pragma option push -b -w-inl
    
    #include <utilcls.h>
    #if !defined(__UTILCLS_H_VERSION) || (__UTILCLS_H_VERSION < 0x0600)
    //
    // Der Code, der durch das Utility TLIBIMP oder den Import einer einer Typbibliothek 
    // und ActiveX-Funktion von C++Builder erezugt wird, basiert auf bestimmten Versionen 
    // der Header-Datei UTILCLS.H, die sich im Verzeichnis INCLUDE\VCL befindet. Wird eine 
    // ältere Version dieser Datei ermittelt, bentöigen Sie einen Update/Patch.
    //
    #error "Diese Datei erfordert eine neuere Version der Header-Datei UTILCLS.H" \
           "Sie müssen einen Update/Patch für Ihre Kopie von C++Builder durchführen"
    #endif
    #include <olectl.h>
    #include <ocidl.h>
    #if defined(USING_ATLVCL) || defined(USING_ATL)
    #if !defined(__TLB_NO_EVENT_WRAPPERS)
    #include <atl/atlmod.h>
    #endif
    #endif
    
    // *********************************************************************//
    // Forward-Referenz einiger VCL-Typen (zur Vermeidung der Einbeziehung von STDVCL.HPP)    
    // *********************************************************************//
    namespace Stdvcl {class IStrings; class IStringsDisp;}
    using namespace Stdvcl;
    typedef TComInterface<IStrings> IStringsPtr;
    typedef TComInterface<IStringsDisp> IStringsDispPtr;
    
    namespace XXXXX_tlb
    {
    // *********************************************************************//
    // Hilfe-String: YYYYY Type Library Version 1.0
    // Version:    1.0
    // *********************************************************************//
    
    // *********************************************************************//
    // In dieser Typbibliothek deklarierte GUIDS . Es werden folgende         
    // Präfixe verwendet:                                                     
    //   Typbibliotheken     : LIBID_xxxx                                     
    //   CoClasses          : CLSID_xxxx                                      
    //   DISPInterfaces     : DIID_xxxx                                       
    //   Nicht-DISP-Schnittstellen: IID_xxxx                                        
    // *********************************************************************//
    extern __declspec (package) const GUID IID_IYYYYY;
    extern __declspec (package) const GUID CLSID_YYYYY;
    
    interface DECLSPEC_UUID("{BF3D4B02-7CA7-3448-9D8E-02239F917AA0}") IYYYYY;
    typedef TComInterface<IYYYYY, &IID_IYYYYY> IYYYYYPtr;
    
    // *********************************************************************//
    // Deklaration von in der Typbibliothek definierten CoClasses             
    // (HINWEIS: Hier wird jede CoClass zu ihrer Standardschnittstelle        
    // zugewiesen)                                                            
    //                                                                        
    // Die Makros LIBID_OF_ weisen eine LIBID_OF_CoClassName zur GUID dieser  
    // Typbibliothek zu. Dies erleichtert die Aktualisierung von Makros bei   
    // der Änderung von Makronamen.                                           
    // *********************************************************************//
    typedef IYYYYY YYYYY;
    typedef IYYYYYPtr YYYYYPtr;
    
    #define LIBID_OF_YYYYY (&LIBID_XXXXX)
    
    // *********************************************************************//
    // Schnittstelle: IYYYYY
    // Flags:     (4432) Hidden Dual OleAutomation Dispatchable
    // *********************************************************************//
    interface IYYYYY  : public IDispatch
    {
    public:
      // [9] Ermöglicht den Zugriff auf die Benuteroberflächeneigenschaften.
      virtual HRESULT STDMETHODCALLTYPE get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterface** retval/*[out,retval]*/) = 0;
      // [10] Laden einer vollständigen Arbeitsumgebung(Messkonfig, OnlineVis, Scripts,...).
      virtual HRESULT STDMETHODCALLTYPE OpenWorkspace(BSTR fileName/*[in]*/) = 0;
      // [7] Zeigt die Programmoberfläche.
      virtual HRESULT STDMETHODCALLTYPE Show(void) = 0;
    
    #if !defined(__TLB_NO_INTERFACE_WRAPPERS)
    
      XXXXX_tlb::IGraphicalUserInterface* __fastcall get_GraphicalUserInterface(void)
      {
        XXXXX_tlb::IGraphicalUserInterface* retval;
        OLECHECK(this->get_GraphicalUserInterface((XXXXX_tlb::IGraphicalUserInterface**)&retval));
        return retval;
      }
    
      __property   XXXXX_tlb::IGraphicalUserInterface* GraphicalUserInterface = {read = get_GraphicalUserInterface};
    
    #endif //   __TLB_NO_INTERFACE_WRAPPERS
    
    };
    // *********************************************************************//
    // SmartIntf: TCOMIYYYYY
    // Schnittstelle: IYYYYY
    // *********************************************************************//
    template <class T /* IYYYYY */ >
    class TCOMIYYYYYT : public TComInterface<IYYYYY>, public TComInterfaceBase<IUnknown>
    {
    public:
      TCOMIYYYYYT() {}
      TCOMIYYYYYT(IYYYYY *intf, bool addRef = false) : TComInterface<IYYYYY>(intf, addRef) {}
      TCOMIYYYYYT(const TCOMIYYYYYT& src) : TComInterface<IYYYYY>(src) {}
      TCOMIYYYYYT& operator=(const TCOMIYYYYYT& src) { Bind(src, true); return *this;}
    
      HRESULT         __fastcall get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterface** retval/*[out,retval]*/);
      HRESULT         __fastcall get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterfacePtr* retval/*[out,retval]*/);
      XXXXX_tlb::IGraphicalUserInterfacePtr __fastcall get_GraphicalUserInterface(void);
      HRESULT         __fastcall OpenWorkspace(BSTR fileName/*[in]*/);
      HRESULT         __fastcall Show(void);
    
      __property   XXXXX_tlb::IGraphicalUserInterfacePtr GraphicalUserInterface = {read = get_GraphicalUserInterface};
    
    };
    typedef TCOMIYYYYYT<IYYYYY> TCOMIYYYYY;
    
    // *********************************************************************//
    // DispIntf:  IYYYYY
    // Flags:     (4432) Hidden Dual OleAutomation Dispatchable
    // *********************************************************************//
    template<class T>
    class IYYYYYDispT : public TAutoDriver<IYYYYY>
    {
    public:
      IYYYYYDispT(){}
    
      IYYYYYDispT(IYYYYY *pintf)
      {
        TAutoDriver<IYYYYY>::Bind(pintf, false);
      }
    
      IYYYYYDispT(IYYYYYPtr pintf)
      {
        TAutoDriver<IYYYYY>::Bind(pintf, true);
      }
    
      IYYYYYDispT& operator=(IYYYYY *pintf)
      {
        TAutoDriver<IYYYYY>::Bind(pintf, false);
        return *this;
      }
    
      IYYYYYDispT& operator=(IYYYYYPtr pintf)
      {
        TAutoDriver<IYYYYY>::Bind(pintf, true);
        return *this;
      }
    
      HRESULT BindDefault()
      {
        return OLECHECK(Bind(CLSID_YYYYY));
      }
    
      HRESULT BindRunning()
      {
        return BindToActive(CLSID_YYYYY);
      }
    
      XXXXX_tlb::IScriptingWorkspace* __fastcall get_ScriptingWorkspace(void);
      HRESULT         __fastcall get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterface** retval/*[out,retval]*/);
      XXXXX_tlb::IGraphicalUserInterface* __fastcall get_GraphicalUserInterface(void);
      HRESULT         __fastcall OpenWorkspace(BSTR fileName/*[in]*/);
      HRESULT         __fastcall Show();
    
      XXXXX_tlb::IUserSettings* __fastcall get_UserSettings(void);
    
      __property   XXXXX_tlb::IGraphicalUserInterface* GraphicalUserInterface = {read = get_GraphicalUserInterface};
    
    };
    typedef IYYYYYDispT<IYYYYY> IYYYYYDisp;
    
    // *********************************************************************//
    // SmartIntf: TCOMIYYYYY
    // Schnittstelle: IYYYYY
    // *********************************************************************//
    template <class T> HRESULT __fastcall
    TCOMIYYYYYT<T>::get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterface** retval/*[out,retval]*/)
    {
      return (*this)->get_GraphicalUserInterface(retval);
    }
    
    template <class T> HRESULT __fastcall
    TCOMIYYYYYT<T>::get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterfacePtr* retval/*[out,retval]*/)
    {
      return (*this)->get_GraphicalUserInterface(IGraphicalUserInterface**)retval);
    }
    
    template <class T> XXXXX_tlb::IGraphicalUserInterfacePtr __fastcall
    TCOMIYYYYYT<T>::get_GraphicalUserInterface(void)
    {
      XXXXX_tlb::IGraphicalUserInterface* retval;
      OLECHECK(this->get_GraphicalUserInterface((XXXXX_tlb::IGraphicalUserInterface**)&retval));
      return (XXXXX_tlb::IGraphicalUserInterfacePtr)retval;
    }
    
    template <class T> HRESULT __fastcall
    TCOMIYYYYYT<T>::OpenWorkspace(BSTR fileName/*[in]*/)
    {
      return (*this)->OpenWorkspace(fileName);
    }
    
    template <class T> HRESULT __fastcall
    TCOMIYYYYYT<T>::Show(void)
    {
      return (*this)->Show();
    }
    
    // *********************************************************************//
    // DispIntf:  IYYYYY
    // Flags:     (4432) Hidden Dual OleAutomation Dispatchable
    // *********************************************************************//
    template <class T> HRESULT __fastcall
    IYYYYYDispT<T>::get_GraphicalUserInterface(XXXXX_tlb::IGraphicalUserInterface** retval/*[out,retval]*/)
    {
      _TDispID _dispid(*this, OLETEXT("GraphicalUserInterface"), DISPID(9));
      TAutoArgs<0> _args;
      return OutRetValSetterPtr((LPDISPATCH*)(XXXXX_tlb::IGraphicalUserInterface**)retval /*[VT_USERDEFINED:2]*/, _args, OlePropertyGet(_dispid, _args));
    }
    
    template <class T> XXXXX_tlb::IGraphicalUserInterface* __fastcall
    IYYYYYDispT<T>::get_GraphicalUserInterface(void)
    {
      XXXXX_tlb::IGraphicalUserInterface* retval;
      this->get_GraphicalUserInterface((XXXXX_tlb::IGraphicalUserInterface**)&retval);
      return retval;
    }
    
    template <class T> HRESULT __fastcall
    IYYYYYDispT<T>::OpenWorkspace(BSTR fileName/*[in]*/)
    {
      _TDispID _dispid(*this, OLETEXT("OpenWorkspace"), DISPID(10));
      TAutoArgs<1> _args;
      _args[1] = fileName /*[VT_BSTR:0]*/;
      return OleFunction(_dispid, _args);
    }
    
    template <class T> HRESULT __fastcall
    IYYYYYDispT<T>::Show()
    {
      _TDispID _dispid(*this, OLETEXT("Show"), DISPID(7));
      return OleFunction(_dispid);
    }
    
    };     // namespace XXXXX_tlb
    
    #if !defined(NO_IMPLICIT_NAMESPACE_USE)
    using  namespace XXXXX_tlb;
    #endif
    
    #pragma option pop
    


  • Permana schrieb:

    Ich setze Breakpoints an den Zeilen von utilcls_oleexc.cpp und der Debugger lehnt sie alle als ungültig ab. Also habe ich die Lösung von aucidia eigentlich noch gar nicht vollständig realisiert. 👎

    Das liegt daran, daß ich für dieses Unit die Debug-Info explizit deaktiviert habe (durch ein #pragma am Anfang der Datei), und das ist auch Sinn der Sache (sonst verwirrt dich der Debugger im Falle einer Exception, indem er in utilcls_oleexc.cpp landet). Und mein Benutzername ist audacia.

    Permana schrieb:

    ich freue mich weiterhin auf Feedback und habe nun das File in reduzierter Form vorbereitet. Bitte schön:
    [...]

    Ah, das ist hilfreich. Der Header definiert also sowohl ein Early-binding- als auch ein Late-binding-Interface. Das Early-binding-Interface verwendest du, indem du einfach TCOMIYYYYY anstelle von IYYYYYDisp benutzt. Der korrekte Weg, um HRESULT-Rückgabewerte explizit zu überprüfen, ist dann

    CheckSafecallResult (OpenWorkspace (WideString (someString).c_bstr ()));
    

    .
    Wenn du utilcls_oleexc.cpp eingebunden hast, kannst du statt CheckSafecallResult() auch OLECHECK() verwenden.



  • Hallo audacia,

    ich kann in unserem Code keinerlei Referenz zum Wort "IYYYYYDispT" finden.
    Daher verstehe ich dich nur begrentzt.

    Bis jetzt haben wir folgendes da und da im Code, was an sich für mich schlüssig ist:

    interface IYYYYY : public IDispatch { ... }
    IYYYYY m_XXXXX; //unser wichtigster Schlüsselpointer bis jetzt.
    extern __declspec (package) const GUID CLSID_YYYYY;
    HRESULT hr = CoInitialize(NULL);
    IUnknown pUnknown = NULL;
    hr = CoCreateInstance(CLSID_YYYYY , NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void
    )&pUnknown);
    hr = pUnknown->QueryInterface(IID_IYYYYY, (void**)&m_XXXXX);
    m_XXXXX->OpenWorkspace(StringToOleStr(myFileName));

    Meinst du nun, ich soll folgendes zum Code addieren?

    TCOMIYYYYY *myYYYYYCom;
    myYYYYYCom = new TCOMIYYYYY (m_XXXXX, false) // m_XXXXX kommt von oben.
    myYYYYYCom->OpenWorkspace(StringToOleStr(myFileName));

    Vielleicht fehlt mir Wissen von COM Technology. Wie bringe ich IYYYYY in Verbindung mit TCOMIYYYYY? Und welcher Parameter hat bis jetzt den Weg zu IYYYYYDisp geebnet?

    Gruß,
    Permana



  • Permana schrieb:

    Bis jetzt haben wir folgendes da und da im Code, was an sich für mich schlüssig ist:

    interface IYYYYY : public IDispatch { ... }
    IYYYYY m_XXXXX; //unser wichtigster Schlüsselpointer bis jetzt.
    extern __declspec (package) const GUID CLSID_YYYYY;
    HRESULT hr = CoInitialize(NULL);
    IUnknown pUnknown = NULL;
    hr = CoCreateInstance(CLSID_YYYYY , NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void
    )&pUnknown);
    hr = pUnknown->QueryInterface(IID_IYYYYY, (void**)&m_XXXXX);

    Uah.
    Du benutzt bereits das Early-binding-Interface. Ich dachte, du würdest Late Binding verwenden, weil ich mich an deinen Post erinnerte:

    Permana schrieb:

    audacia schrieb:

    Könntest du mal die Deklaration von OpenWorkspace() in der Headerdatei zeigen?

    Hilft dies aus dem Headerfile der automatisch erzeugte Typelibrary?

    (mit
    typedef LONG HRESULT;)
    HRESULT __fastcall OpenWorkspace(BSTR fileName/[in]/);

    template <class T> HRESULT __fastcall
    IBlablaBlaDispT<T>::OpenWorkspace(BSTR fileName/[in]/)
    {
    _TDispID _dispid(this, OLETEXT("OpenWorkspace"), DISPID(10));
    TAutoArgs<1> _args;
    _args[1] = fileName /
    [VT_BSTR:0]*/;
    return OleFunction(_dispid, _args);
    }

    Die Funktionsdefinition, die du da zitiert hast, ist fürs Late-binding-Interface gedacht, daher dachte ich, daß du sie auch verwendest 😉

    Permana schrieb:

    Meinst du nun, ich soll folgendes zum Code addieren?
    [...]

    Nein. Bisher scheinst du in der Handhabung von COM der Visual C++-Variante zu folgen (vermutlich weil deine Code-Beispiele für Visual C++ gedacht sind). Das geht in C++Builder zwar auch, aber es ist sehr viel umständlicher als nötig. In C++Builder würde folgender Code

    IYYYYY *m_XXXXX; //unser wichtigster Schlüsselpointer bis jetzt.
    extern __declspec (package) const GUID CLSID_YYYYY;
    HRESULT hr = CoInitialize(NULL);
    IUnknown *pUnknown = NULL;
    hr = CoCreateInstance(CLSID_YYYYY , NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown);
    hr = pUnknown->QueryInterface(IID_IYYYYY, (void**)&m_XXXXX);
    m_XXXXX->OpenWorkspace(StringToOleStr(myFileName));
    

    zu dem hier (allerdings ungetestet, da ich deine Typbibliothek nicht habe):

    TCOMIAccessible m_XXXXX = CoYYYYY::Create ();
        CheckSafecallResult (m_XXXXX.OpenWorkspace(WideString (myFileName).c_bstr ()));
    

    Am Ende deiner Typbibliothek müßte ein CoClass-Wrapper für das Automationsobjekt zu finden sein, etwa so etwas:

    // *********************************************************************//
    // COCLASS STANDARD-INTERFACE ERZEUGUNG
    // CoClass : YYYYY
    // Interface: TCOMIYYYYY
    // *********************************************************************//
    typedef TCoClassCreatorT<TCOMIYYYYY, IYYYYY, &CLSID_YYYYY, &IID_IYYYYY> CoYYYYY;
    

    Diese CoClass vereinfacht die Erstellung von COM-Objekten signifikant.

    Wenn du das machst, im Normalfall alles funktioniert und immer Fehlerfall immer noch keine Exception auftritt, dann wird auf der anderen Seite auch keine geworfen, und es liegt in irgendeiner Weise an der Benutzung. Jedenfalls gibt es keinen Weg mehr, auf dem C++Builder die Exception irgendwie "verschlucken" könnte.



  • Ich habe folgendes an die richtigen Plätze gebracht und leider nur Access Violation Exception erhalten, was vermutlich nur eine interne Exception ist.

    hr = CoCreateInstance(CLSID_YYYYY , NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown);
    TCOMIAccessible m_XXXXX = CoYYYYY::Create ();
    (m_XXXXX.OpenWorkspace(WideString (myFileName).c_bstr ()));

    CheckSafecallResult musste ich raus lassen, weil
    [Linker Fehler] Unresolved external 'System::__linkproc__ __fastcall CheckAutoResult()' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\LIB\RELEASE\VCLE.LIB|syssupp

    Mittlerweile habe ich der Liferfirma geschrieben, wie sie sich unterschiedliche COM Traces im Falle von C# und C++ Applikation erklären.

    Gruß,
    Permana



  • Permana schrieb:

    hr = CoCreateInstance(CLSID_YYYYY , NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown); 
    TCOMIAccessible m_XXXXX = CoYYYYY::Create (); 
    (m_XXXXX.OpenWorkspace(WideString (myFileName).c_bstr ()));
    

    Die erste Zeile ist, wenn ich es richtig sehe, überflüssig. In der zweiten Zeile habe ich einen Fehler gemacht: es muß natürlich nicht TCOMIAccessible, sondern TCOMIYYYYY heißen.

    Was die AV angeht: könntest du - nach Berichtigung obengenannter Fehler - einmal zeigen, wie die entsprechende Stelle deines Programmcodes wirklich aussieht?

    Permana schrieb:

    CheckSafecallResult musste ich raus lassen, weil
    [Linker Fehler] Unresolved external 'System::__linkproc__ __fastcall CheckAutoResult()' referenced from C:\PROGRAM FILES\BORLAND\CBUILDER6\LIB\RELEASE\VCLE.LIB|syssupp

    Das ist ein Bug in C++Builder 6, der in C++Builder 2006 behoben wurde. (Ein typisches Problem, wenn man mit acht Jahre alter Software arbeitet: man kämpft andauernd mit Problemen, die eigentlich längst behoben wurden. => Gelegentlich über ein Upgrade nachdenken.) Du kannst es folgendermaßen selbst beheben (irgendwo in deinem Quellcode einzufügen):

    #pragma alias "@System@CheckSafecallResult$qqrl" \
          = "@System@@CheckAutoResult$qqrl"
    


  • Hallo audacia,

    ich habe Deine Korrekturen berücksichtigt. Es ging dann glatt.
    Leider wieder ohne Exception. Langsam langsam bleibt mir nur der Glaube, dass da keine Exceptions geworfen werden.

    Ich habe zufällig auch ein kleines Testprogramm in C++, das in Visual Studio
    läuft. Da fällt das Progrämmchen in Exceptionzweig a la catch(...). Ich weiss
    bisher keine Exceptionname, was ich da rein schreiben könnte, um die Botschaft
    auszulesen, um sicher zu sein, dass es sich um meine Exception wegen dem bösen
    Workspacefile handelt (und keine andere).

    Es ist sehr nett, dass du so viel Geduld bisher mit mir gehabt hast. Ich warte
    nebenbei auf die Antwort der Interfaceentwickler.

    viele Grüsse,
    Permana



  • Permana schrieb:

    ...
    Ich habe zufällig auch ein kleines Testprogramm in C++, das in Visual Studio
    läuft. Da fällt das Progrämmchen in Exceptionzweig a la catch(...).
    ...
    Es ist sehr nett, dass du so viel Geduld bisher mit mir gehabt hast. Ich warte
    nebenbei auf die Antwort der Interfaceentwickler.

    Die Lieferantenfirma hat nichts besseres zu sagen, außer daß ich catch(...) einsetzen sollte, was natürlich einer der ersten Schritte ist, die ich unternommen habe.

    Ich glaube, wir brauchen neue Compiler und Umgebung.

    Gruß,
    Permana



  • Permana schrieb:

    Ich glaube, wir brauchen neue Compiler und Umgebung.

    Ich sagte doch bereits, daß der Fehler eigentlich nicht vom Compiler verursacht sein kann. Es wird irgendwie an den Daten liegen, die du übergibst.

    Hast du ein vollständiges Codebeispiel, das, sofern mit Visual C++ übersetzt, eine Exception verursacht?
    Und könntest du exakt dasselbe Codebeispiel einmal mit dem C++Builder übersetzen?



  • Hallo audacia,

    nein das habe ich nicht probiert. Das ist eine gute Idee. Nur das ist eine Windowsanwendung. Mein erster Versuch ist an die Kompilierung einer Resourcedatei gescheitert. Ich hoffe, ich kann morgen da besser voran kommen.

    Gruß,
    Permana



  • Hi,

    zum Einen hat sich der Import zu Borland C++ als aufwendig erwiesen (und ich wurde davon abgeraten). Zum Anderen tritt jetzt die Exception da auch nicht auf. (ich wollte die ganze Zeit klar stellen, ob es sich um die eine Exception geht). Also bleibt es bei der Verdacht, dass die Exceptions die .Net Grenze nicht verlassen.

    Jetzt habe ich den ganzen Wissenstand in einem Bericht verfasst und werde das beim nächsten Treffen zur Diskussion stellen. Der Lieferant muß sagen, ob sie das selber schon mal mit C++ Applikationen untersucht haben.

    vielen Dank und schönes Wochenende



  • ich werde mal verrückt.
    Heute habe ich meinem Betreuer das andere C++ Prgorgamm gezeigt und am Anfang ist der Code gar nicht in Catch-Zweig gesprungen. Dann habe ich die neue Type Library vom Lieferanten importiert und neu kompiliert. Prompt sieht man die gleiche Exception, die auch bei C# Beispielprogramm zum Schein kommt. Wahnsinn.

    Wird es leicht sein, das Programm in eine neue Borland Umgebung zu importieren?

    Kriege ich von Borland Fragen für Version 6 beantwortet, wenn der Vertrag paar Jahre zurück liegt?

    Ich versuche jetzt eine Applikation mit nur paar Zeilen zu schreiben und schauen, was da passiert. Gott oh Gott.

    Gruß,
    Permana



  • Permana schrieb:

    Wird es leicht sein, das Programm in eine neue Borland Umgebung zu importieren?

    Das hängt davon ab,

    • wie umfangreich dein Programm ist,
    • wie viele und welche 3rd-party-Komponenten du benutzt, und
    • wie du mit Strings umgehst. Rufst du viele WinAPI-Funktionen direkt auf? Verwendest du die C-Standardbibliothek oder andere C- oder C++-Bibliotheken intensiv?

    So ganz pauschal kann ich es jedenfalls nicht sagen. Ich hatte meist keine große Mühe damit.

    Du kannst es ja einfach mal ausprobieren: hier gibt es eine Demoversion.

    Permana schrieb:

    Kriege ich von Borland Fragen für Version 6 beantwortet, wenn der Vertrag paar Jahre zurück liegt?

    "Ein paar Jahre?" Du meinst "acht", ja? 🙂
    Borland hat Delphi und C++Builder sowie die anderen Tools wurden zwei Jahren an Embarcadero verkauft, wo sie jetzt weiterentwickelt werden. (Borland selbst ist zwischenzeitlich von MicroFocus geschluckt worden, das spielt aber dank Embarcadero keine Rolle mehr.) Du kannst dich an den Embarcadero-Support wenden, oder aber eine Frage in den Newsgroups stellen. Es würde mich aber nicht wundern, wenn dir dort als sinnvollste Lösung des Problemes ein Upgrade nahegelegt wird.



  • Hallo audacia,

    vielen Dank noch mal für die Aufmerksamkeit. Wir werden die Demoversion testen und den Aufwand abschätzen.

    audacia schrieb:

    Permana schrieb:

    Wird es leicht sein, das Programm in eine neue Borland Umgebung zu importieren?

    Das hängt davon ab,

    • wie umfangreich dein Programm ist,
    • wie viele und welche 3rd-party-Komponenten du benutzt, und
    • wie du mit Strings umgehst. Rufst du viele WinAPI-Funktionen direkt auf? Verwendest du die C-Standardbibliothek oder andere C- oder C++-Bibliotheken intensiv?

    Es werden nicht viele WinAPI-Aufrufe gemacht und die meisten Komponenten sind in unserer Obhut.

    Ich habe heute mit meinem Betreuer eine gehörige Portion von Zeit auf die drei Applikationen investiert. Die beiden anderen tun nichts weiter als den Rückgabewert in eine Exception umzuwandeln.

    Dank Deiner Ratschläge wissen wir nun, dass wir auch einen negativen Rückgabewert erhalten können, wenn wir so vorgehen:

    TCOMIXXXXX m_XXXXX = CoYYYYY::Create ();
    CheckSafecallResult(m_XXXXX.OpenWorkspace(StringToOleStr(myFileName)));

    Ich habe eine kleine Borland Applikation geschrieben. Es funktionert auch sogar mit deinem Code (utilcls_oleexc.cpp), dass da eine Exception geworfen wird. Nur wenn ich die Beschreibung von CheckSafecallResult im Netz lese, sieht so aus, als da auch nichts weiter gemacht wird, als eine künstliche Exception zu produzieren. Ursprünglich dachte ich, die Exception vom Lieferantencode würde zu unserem Code gelangen.

    Jedenfalls kriegen wir für den bösen Workspace folgende Exception:
    "Die angegebene Umwandlung ist ungültig"

    Die beiden anderen Beispielapplikationen melden so was wie:
    "Beim Aufruf einer COM-Komponente wurde ein HRESULT E_FAIL-Fehler zurückgegeben."

    Mir scheint keine von beiden liefert eine Exception von der Tiefe des COM-Programms. Die C++ App bedient sich z.B. mit
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));

    Hast du eine Vorstellung, woher "Die angegebene Umwandlung ist ungültig" kommen könnte? Ich kann ja in deinem Code keine Breakpoints setzen.

    Dir einen schönen Abend, 🙂
    Permana



  • Permana schrieb:

    Ich habe eine kleine Borland Applikation geschrieben. Es funktionert auch sogar mit deinem Code (utilcls_oleexc.cpp), dass da eine Exception geworfen wird. Nur wenn ich die Beschreibung von CheckSafecallResult im Netz lese, sieht so aus, als da auch nichts weiter gemacht wird, als eine künstliche Exception zu produzieren. Ursprünglich dachte ich, die Exception vom Lieferantencode würde zu unserem Code gelangen.

    Das Problem ist die COM-Schnittstelle dazwischen. COM ist ein compiler- und sprachunabhängiges Objektmodell, das heißt, ein COM-Objekt, das in C# erstellt wurde, kann auch von Visual C++, C++Builder oder Delphi aus verwendet werden, und ebenso umgekehrt. Nun hat jeder Compiler allerdings seine eigenen Vorstellungen davon, wie Exception-Handling funktionieren soll. Bei jedem Compiler sind Exceptions und alles, was damit einhergeht (insbesondere das Stack-Unwinding) etwas anders implementiert, mithin: nicht kompatibel. Wenn du in gewöhnlichem Visual C++-Code eine Exception wirfst und den Code von einem C++Builder-Programm aus aufrufst, bekommst du bestenfalls eine "External Exception"; schlimmstenfalls stürzt dein Programm einfach ab.

    Aus diesem Grunde gilt die Regel, daß der Aufruf einer Methode eines COM-Objektes keine Sprach-Exception verursachen darf. Stattdessen wird ein Fehlercode (-> HRESULT) zurückgegeben, und die Fehlerinformation (die Exception-Nachricht) wird mittels SetErrorInfo() zentral abgespeichert.

    Das bedeutet, daß man beim Implementieren einer COM-Schnittstelle spezielle Vorkehrungen treffen muß:

    class MyCOMObject : public IMyCOMInterface
    {
      ...
    public:
      HRESULT MyFunction (int arg);
    };
    
    HRESULT MyCOMObject::MyFunction (int arg)
    {
      try
      {
        // tatsächlicher Code
        return S_OK;
      }
      catch (std::exception& e)
      {
        return SetExceptionInfo (e); // SetExceptionInfo() sei irgendeine Helferfunktion
      }
      catch (Exception& e)
      {
        return SetExceptionInfo (e);
      }
    }
    

    Entsprechend muß man auch beim Aufruf den Rückgabewert überprüfen. Helferfunktionen wie CheckSafecallResult() erzeugen nun im Fehlerfalle tatsächlich wieder eine Exception, so daß sich das Programm so verhält, als hätte die COM-Schnittstellenfunktion tatsächlich selbst eine Exception geworfen.

    Wenn du mit Delphi arbeitest, übernimmt der Compiler diese ganze Arbeit für dich. In Delphi sähe obiger Code etwa so aus:

    type
      TMyObject = class (TAutoObject, IMyCOMInterface)
        ...
        procedure MyFunction (Arg: Integer); safecall; // <--
      end;
    
    procedure TMyObject.MyFunction (Arg: Integer);
    begin
      // tatsächlicher Code
    end;
    

    Die "safecall"-Aufrufkonvention sorgt dafür, daß der Compiler automatisch Exception-Guards generiert und einen Fehlercode zurückgibt. Und wenn du in Delphi eine "safecall"-Funktion aufrufst, wird auch automatisch CheckSafecallResult() zur Überprüfung des Fehlercodes aufgerufen und ggf. eine Exception geworfen, ohne daß du etwas dafür tun müßtest. In C++ bekommt man das leider nicht ganz geschenkt.

    Permana schrieb:

    Jedenfalls kriegen wir für den bösen Workspace folgende Exception:
    "Die angegebene Umwandlung ist ungültig"

    Die beiden anderen Beispielapplikationen melden so was wie:
    "Beim Aufruf einer COM-Komponente wurde ein HRESULT E_FAIL-Fehler zurückgegeben."

    Mir scheint keine von beiden liefert eine Exception von der Tiefe des COM-Programms.

    Doch; die erste Meldung dürfte exakt der Exception-Meldung entsprechen.

    Der Unterschied kommt daher, daß ein bloßer Fehlercode (HRESULT ist nur ein Integerwert) natürlich keine detaillierte Fehlermeldung enthalten kann. Aus einem HRESULT-Wert lassen sich nur generische Systemfehlermeldungen wie "Fehler", "Das System kann die angegebene Datei nicht finden" und dergleichen ableiten. Um an die tatsächliche Exception zu kommen - vorausgesetzt, der Erzeuger hat sie mittels SetErrorInfo() hinterlegt -, muß man das Pendant GetErrorInfo() aufrufen. Genau das macht auch CheckSafecallResult() bzw. läßt machen, indem es folgende Funktion aus ComObj.pas aufruft:

    procedure SafeCallError(ErrorCode: Integer; ErrorAddr: Pointer);
    var
      ErrorInfo: IErrorInfo;
      Source, Description, HelpFile: WideString;
      HelpContext: Longint;
    begin
      HelpContext := 0;
      if GetErrorInfo(0, ErrorInfo) = S_OK then
      begin
        ErrorInfo.GetSource(Source);
        ErrorInfo.GetDescription(Description);
        ErrorInfo.GetHelpFile(HelpFile);
        ErrorInfo.GetHelpContext(HelpContext);
      end;
      raise EOleException.Create(Description, ErrorCode, Source,
        HelpFile, HelpContext) at ErrorAddr;
    end;
    

    Permana schrieb:

    Hast du eine Vorstellung, woher "Die angegebene Umwandlung ist ungültig" kommen könnte? Ich kann ja in deinem Code keine Breakpoints setzen.

    Aus meinem Code kommt sie auch nicht 😉 Das sollte schon die Original-Fehlermeldung sein. Du kannst es aber verifizieren, indem du in den Projektoptionen "Mit Debug-Bibliotheken linken" aktivierst und einen Breakpoint in obige Funktion in ComObj.pas setzt.


Anmelden zum Antworten