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



  • 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.



  • audacia schrieb:

    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.

    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.

    Vielen Dank noch mal für alle die Kommentar, z.B. für GetErrorInfo(). Und für den Tipp um Breakpoints. 👍

    Mittlerweile weiss ich, dass die Meldung "Die angegebene Umwandlung ist ungültig" vom Originalexception kommt. Denn ich habe die Applikation standalone aufgerufe und das böse Workspace geladen und dann die Traces mir angeschaut. Also ohne Deinen Code. Da sieht man folgendes:

    04.08.2010 10:21:22.463 E EXCEPTION GlobalExceptionHandler: Unerwartete Excpeption: System.InvalidCastException: Die angegebene Umwandlung ist ungültig.

    Jetzt habe ich mich auch an den Satz meines Kollegen erinnert, daß COM nur Long oder String rausgeben darf. Da sind wir wieder bei deinen Erklärungen um unterschiedlichen Exceptionklassen bei unterschiedlichen Compiler.

    Ich werden nun die Behandlung von SimpleApplication zu meiner richtigen Applikation übertragen und hoffen, dass hier ein Baustein für die Zukunft des Codes liegt.

    viele Grüsse,
    Permana



  • Permana schrieb:

    Ich werden nun die Behandlung von SimpleApplication zu meiner richtigen Applikation übertragen und hoffen, dass hier ein Baustein für die Zukunft des Codes liegt.

    Leider erhalten wir, egal mit welcher IF-Klasse, immer einen positiven Rückgabewert. Ich habe all die Projeltoptionen der einfachen und komplizierten Klasse mit einander verglichen und auch mal einander angeglichen. Weder kann ich das gute Verhalten der einfachen Applikation mit dem Setting der anderen zerstören. Noch kann ich das ungute Verhalten unserer Applikation mit dem Setting des anderen verbessern.

    Es gibt einen anderen Use Case, wo ich negativen Rückgabewert erwarte. Diese verhält sich erwartungs gemäß in unserer Applikation. Aber da geht es auch nicht um das Oberpointer des COM Interface, sondern von einer der vielen Subinterfaces. (um dies testen zu können, habe ich ein gutes Workspace geladen)

    Ich werde jetzt unser Programm bis zum GehtNichtMehr abkürzen und schauen, wie das Verhalten ist.

    Gruß,
    Permana



  • Permana schrieb:

    Ich werde jetzt unser Programm bis zum GehtNichtMehr abkürzen und schauen, wie das Verhalten ist.

    ok, dies hat viel geholfen. Das Löschen aller Files aus dem Projekt und das Addieren der zwei Dateien der SimpleApplication hat gezeigt, dass auch hier ein vernünftiger Rückgabewert möglich ist.

    Dann habe ich den Ansatz verglichen (und ich hatte eigentlich schon vorher damit begonnen) und fand heraus, dass in unserer Applikation das Laden der COM DLL in dem Konstruktor eines Members einer TFORM, das Hauptfenster also, aufgerufen von FormCreate() passiert.

    Jetzt habe ich den Konstruktor verkürzt und rufe in FormCreate() danach noch eine Init Funktion, die DLL lädt. Später nach dem Drucken auf Button xyz wird das böse Workspace geladen und schon erhalte ich einen negativen Rückgabewert.

    So sieht nun der Code aus:

    void __fastcall THauptfenster::FormCreate(TObject *Sender)
    {
    …
      if (m_xxxxxIF == NULL)
      {
        m_xxxxxIF = new XXXXX_IF();
      }
      m_xxxxxIF->myInitCall();
    }
    
    Mit
    
    void XXXXX_IF::myInitCall(void)
    {
    …
          HRESULT hr = CoInitialize(NULL);
          IUnknown *pUnknown = NULL;
          hr = CoCreateInstance(... (void**)&pUnknown);
          hr = pUnknown->QueryInterface(...(void**)&m_yyyyy);
    …
    }
    
    void __fastcall TKonfigFenster::XXXXXSetBtnClick(TObject *Sender)
    {
         if (m_xxxxxIF)
         {
             if (m_xxxxxIF->OpenConfigXXXXX(param) == true)
             { ... }
         ... 
        }
    }
    
    bool XXXXX_IF::OpenConfigXXXXX(paramType param)
    {
       ...
       HRESULT hr = m_yyyyy->OpenWorkspace(StringToOleStr("C:\\CorruptedFile"));
    // morgen wird es so aussehen:
    // CheckSafecallResult(m_yyyyy->OpenWorkspace(StringToOleStr("C:\\CorruptedFile")));
       ...
    }
    

    Ich bin so was von guter Laune. 🙂

    Gruß,
    Permana



  • Das war doch nicht die Erklärung. Es geht um einen Bug bei COM. Wenn vorher Hide() aufgerufen wurde, liefert OpenWorkspace() immer 0. Wenn nicht dann negativen Wert. So habe ich gerade eben endlich meine Exception bekommen. Den Bug habe ich dem Lieferanten berichtet.

    Das war ein langer Weg. 🙄



  • Hallo audacia,

    ich wollte mich herzlichst bei dir bedanken. Für deine Unterstüzung. Ohne die Gespräche mit dir und das File, was du mir zur Verfügung gestellt hast, wäre es nicht zum Erfolg gekommen. Ich wünsche dir alles Gute und viel Erfolg, vor allem bei dem was dir wichtig ist.

    Viele Grüsse,
    Permana


Anmelden zum Antworten