catch() wird nicht abgearbeitet
-
Hallo,
ich möchte gerne mit Indy idSMTP Mails versenden. Klappt auch wunderbar, solange kein Fehler auftritt. Tritt doch ein Fehler auf, möchte ich die Fehlerroutine selber vorgeben, dazu folgender Code:
try { F_Main->SMTPServer->Connect(); } catch(char *str) { fprintf(protokoll,"Error: %s; %s \n", str, emailadresse); error = 1; } if(error == 0) { F_Main->Status->Caption = "Verbindung steht, Mail wird versendet"; Application->ProcessMessages(); F_Main->SMTPServer->Send(F_Main->MailMessage); F_Main->SMTPServer->Disconnect(); F_Main->Status->Caption = "Mail succesful send"; Application->ProcessMessages(); }
Wenn ich das Programm (außerhalb der Borland Entwicklungsumgebung) laufen lasse und absichtlich ein Connect mit falschen Login-Daten versuche, dann wird eine Exception ausgelöst, jedoch nicht mein Code unter catch() ausgeführt, sondern ich bekomme stattdessen eine Messagebox mit dem Fehler.
Da das Programm automatisch hunderte von Dateien pro Tag überprüfen und diese ggf. per Mail weiterleiten soll, ist es ziemlich blöd, wenn der Ablauf des Programmes unterbrochen wird, weil eine blöde Messagebox darauf wartet, weggeklickt zu werden. Und mit Exceptions, z.B. aufgrund einer kurzen Nichterreichbarkeit des Servers, muss ich ja durchaus umgehen können ohne das mein komplettes Programm deswegen angehalten wird.
Das Programm soll mir nur eine Fehlermeldung in die Protokolldatei schreiben.
Das macht es aber nicht, es kommt nur diese Messagebox.Hat vielleicht einer eine Idee, warum mein Catch()-Code nicht ausgeführt wird?
Danke für Eure Hilfe.
-
steht bei catch nicht normalerweise sowas:
catch (Exception &E)
?
-
Linnea schrieb:
steht bei catch nicht normalerweise sowas:
catch (Exception &E)
?
Wäre in dem Fall vermutlich richtig.
Aber eine Erklärung an den OP:
Ein catch-Block wird immer nur aufgerufen, wenn es eine Typentsprechung gibt. Unter dem C++ Builder ist das in der Regel eine von der Exception, bei der C++ Standardbibliothek eine von der std::exception abgeleitete Klasse. Keine mir bekannte Bibliothek verwendet char-Zeiger oder grundsätzlich Zeigertypen für Exceptions (wäre auch nicht sinnvoll). Zu guter letzt gibt es noch einen catch-all ("catch(...)", weiter unten im Beispiel sichtbar)
Zudem kann man auch mehrere catch-Blöcke hintereinander definieren, um spezielle Exceptions abzufangen. Dabei ist die Reihenfolge immer vom speziellen zum allgemeinen Fall zu beachten.
Da ich die Exceptionhirachie im C++ Builder noch nicht kenne, hier ein gemischtes Beispiel mit den C++ Standardbibliothek-Exceptions.
// Beispiel, basierend auf der C++ Standardbibliothek, bei deinen Fall wäre
// "Exception" (Basisklasse für VCL-Exceptions) wohl eher relevanttry { // <-- Mehrere Anweisungen die Exceptions werfen können } catch(std::bad_alloc & e) // Behandlung von Fehlern beim Allozieren über new { // std::bad_alloc ist von std::exception abgeleitet // Behandlung } catch(std::exception & e) // Behandlung von Fehlern in der C++ Standardbibliothek { // da allgemeiner als std::bad_alloc muss dieser Block // Behandlung hinter dem Spezialfall liegen (sonst würde // std::bad_alloc statt dessen hier abgehandelt werden) } catch(...) // Behandlung aller anderen Exceptions ohne Typkenntnis { // Behandlung }
Auch noch interessant zu wissen ist, wie man eine Exception weiterreicht (Sprich wenn man zwar einen catch-Block verwendet, aber will das eine Exception dann weitergereicht wird [weitergereicht heißt zum nächsten im Aufrufgraphen höherliegenden try/catch-Block):
void foo() { try { throw std::bad_alloc(); } catch(std::bad_alloc & e) { throw; // weiterreichen bezogen auf den Aufrufgraph } catch(std::exception & e) { // <-- Hier kommt die weitergereichte Exception nicht an, // sondern sie verhält sich wie ungefangende in dem sie // foo() sofort beendet und zum im Aufrufgraphen nächstmöglichen // try-catch läuft } // <-- Jeder Code an der Stelle wird wegen der Exception ignoriert } int main() { try { foo(); } catch(std::exception & e) { // Hier kommt die durchgereichte Exception an } }
-
Hallo,
wenn ich Dich richtig verstanden habe, müsste es dann auf jeden Fall klappen, wenn ich ohne Typkenntnis alle Exceptions abfange mit catch(...), also so:
try { F_Main->SMTPServer->Connect(); } catch(...) { fprintf(protokoll,"Error beim Versenden an %s \n", emailadresse); error = 1; } if(error == 0) { ... }
Funktioniert aber leider nicht, ich bekomme (wieder angemerkt auch außerhalb der Entwicklungsumgebung von Borland) eine Messagebox mit der Fehlermeldung und mein Catch-Block wird nicht abgearbeitet.
-
W.Stecher schrieb:
wenn ich Dich richtig verstanden habe, müsste es dann auf jeden Fall klappen, wenn ich ohne Typkenntnis alle Exceptions abfange mit catch(...), also so:...
Funktioniert aber leider nicht, ich bekomme (wieder angemerkt auch außerhalb der Entwicklungsumgebung von Borland) eine Messagebox mit der Fehlermeldung und mein Catch-Block wird nicht abgearbeitet.
Wenn eine Exception geworfen wird, kannst du diese auch mit dem catch-all abfangen. Könnte es sein, das die Methode garkeine Excepton wirft?
(Wie wäre es mit ein Blick in die garantiert dazugehörende Hilfe?)Die zweite Variante ist: Hast du auf Release gelinkt (an der Arbeit habe ich festgestellt das in der DEBUG-Version immer eine Messagebox aufgeht bei einer Exception, und erst anschließend die normale Behandlung erfolgt).
cu André
-
Meine erste Vermutung war auch, dass kar keine Exception geworfen wird, aber wenn ich das Programm in der Entwicklungsumgebung von Borland starte, dann bekomme ich die Fehlermeldung:
Im Project XMLTest.exe ist eine Exception der Klasse EIdProtocolReplyError aufgetreten. Meldung: '535 5.7.0 Incorrect username or password {mp061}'.Prozess wurde angehalten.
Wenn ich Borland beende und meine XMLTest.exe aus dem Windows-Explorer heraus starte, dann sollte also mein Catch-Block abgearbeitet werden. Passiert aber nicht, stattdessen bekomme ich die Messagebox
5.7.0 Incorrect username or password {mp061}'.
Aber nachdem ich gerade zum x. Mal das Programm auch in der Entwickungsumgebung aufgerufen habe, ist das Brett vorm Kopf geplatzt. Oh, Mann, ich glaube da hätten wir noch Ewigkeiten rästeln können und hätten es nicht geschafft.
Die Lösung:
Wenn ich mich mit falschem Benutzernamen oder falschem Passwort connecte, ist es nicht die Zeile
F_Main->SMTPServer->Connect();
die die Exception wirft, sondern erst die Zeile
F_Main->SMTPServer->Send(F_Main->MailMessage);
Da das Connect() also keine Exception geworfen hat, konnte mein Catch-Block auch nichts abfangen. Error wurde nicht auf 1 gesetzt und der Block
if(error == 0)
wurde abgearbeitet. Dort wurde dann versucht die Mail zu versenden und es kam zur Exception. Da danach aber kein catch-Block mehr existiert, habe ich nichts abgefangen und es kam zur Standard-Windows-Messagebox.
Tata ...Und so funktioniert es genau so, wie ich wollte, keine Messagebox, sondern Entrag ins Fehlerprotokoll:
try { F_Main->SMTPServer->Connect(); F_Main->SMTPServer->Send(F_Main->MailMessage); } catch(...) { fprintf(protokoll,"Error beim Versenden der EMail"); error = 1; } if(error == 0) { F_Main->SMTPServer->Disconnect(); F_Main->Status->Caption = "Mail succesful send"; Application->ProcessMessages(); }
Danke an alle die geholfen haben.
-
Noch eine kleine Ergänzung, falls nochmal einer vor diesem Problem steht.
Wie folgt kann auch die Exception in der Catchanweisung abgefangen und wieder ausgegeben werden:Zum Verständnis:
Mein Hauptformular heißt: F_Main
darauf gibt es ein Label mit Namen "L_Status"
Meine TidSMTP-Komponente heißt: SMTPServer
Meine TidMessage heißt: MailMessageint mailversenden (void) { FILE *protokoll; int error = 0; protokoll = fopen("C:\\wo auch immer\\Datei.txt","a"); try { F_Main->SMTPServer->Connect(); F_Main->SMTPServer->Send(F_Main->MailMessage); } catch(Exception &e) { fprintf(protokoll,"Error: %s",e.Message.c_str()); error = 1; } if(error == 0) { F_Main->SMTPServer->Disconnect(); F_Main->L_Status->Caption = "Mail succesful send"; Application->ProcessMessages(); } return(error); }
-
BTW, nur noch als Anhang. Wenn der Verbindungsaufbau klappt, aber der Versand scheitert, wird die Verbindung nicht geschlossen. Ich weiss nicht ob dieser SMTPServer und die Anwendung damit leben kann aber es wäre wahrscheinlich besser, im Exceptionhandler ebenfalls die Verbindung zu schließen oder sie in einer RAII zu kapseln.
-
W.Stecher schrieb:
Da danach aber kein catch-Block mehr existiert, habe ich nichts abgefangen und es kam zur Standard-Windows-Messagebox.
Eine kleine Anmerkung hierzu: die Exception wird nicht von Windows gefangen (sonst würde dein Programm terminiert), sondern in der Message-Loop der VCL. Diese Exceptions kann man bei Bedarf über TApplication::OnMessage oder TApplicationEvents::OnMessage behandeln.