wie fange ich in C++ Code die Exceptions aus C# COM?
-
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|syssuppDas 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