Ich überarbeite gerade meine Fehlerbehandlung/Fehlersystem in meinem Programm.
Meine Fehlerverarbeitung basierte anfangs hauptsächlich auf Exceptions (try, catch, throw).
Sämtliche Objekte/Ressourcen/Speicher werden durch spezielle Verwalter organisiert, wie z.B. im einfachsten Fall ein SmartPointer/std::shared_ptr, was ein "manuelles" Freigeben in der Regel überflüssig macht, da dies "automatisch" passiert (SmartPointer::Destructor->Release->Referenzzähler fällt auf 0->Delete).
Die Kombination von Exceptions und der "automatischen" Ressourcenverwaltung machte Anfangs die Fehlerbehandlung extrem einfach.
Ich konnte gigantische Funktionen einfach in einem try-Block packen und bei einem throw, egal wo und wie tief in der Funktion, waren bei der Ankunft im catch-Block alle in der Funktion angeforderten Ressourcen wieder korrekt freigegeben. Lief also super!
Mit der Zeit und dem Wachsen des Projektes gab es allerdings immer mehr Situationen, wo z.B. beim "Aufräumen" auch Exceptions geworfen wurden, was dann natürlich kompletter Käse war. Ich habe dann versucht das Exceptionhandling so anzupassen, dass während ein Fehler geworfen wird, keine weiteren Fehler geworfen werden können. Das hat auch nicht immer richtig funktioniert, vor allem bekam ich nachdem die Exception verarbeitet war zahlreiche Fehlermeldungen von diversen Kontrollsystemen (entdeckte Memoryleaks usw.).
Mir ist dann erst aufgefallen, dass ich eigentlich bei fast allen Fehlern das Programm so oder so beende. Nur Bei Exceptions, die ich erwarte und nicht vermeiden kann, wird das Programm fortgesetzt. Die Kapselung der Funktion IDirect3DDevice9::Present ist z.B. so ein Fall, dort wurde eine Exception erwartet und auch direkt abgefangen. Ich habe dann mein Programm genauer untersucht und fand tatsächlich keine Stelle, wo das Programm fortgesetzt wird, ohne das die Exception unmittelbar nach dem throw verarbeitet wird.
Zu Testzwecken habe ich mein Fehlersystem und die Behandlung von Fehlern komplett umgebaut. Exceptions gibt es nicht mehr, also kein try/catch/throw. Wird ein Fehler "gemeldet", was zuvor zu einer Exception geführt hätte, wird dieser angezeigt, gegebenenfalls protokolliert und dann das Programm direkt beendet (TerminateProcess). Damit ein Fehler nicht angezeigt wird und zur Beendigung des Programmes führt, muss man dies dem Fehlersystem zuvor melden, was über ein spezielles Objekt/Klasse geschieht.
Code: Alles auswählen
{
ClassErrorCatcher ErrorCatcher;
m_Direct3DDevice9->Present(Null, Null, Null, Null);
if (0 != ErrorCatcher.Error.Number)
{
return ErrorCatcher.Error.Number;
}
}
Im Prinzip funktioniert jetzt alles wunderbar, das Einzige, was ich unschön finde, ist das sofortige Beenden des Programmes bei einem Fehler ohne "Aufräumen".
Auf der anderen Seite ist ein "korrektes" Aufräumen sehr schwer, wenn zuvor ein unerwarteter Fehler passiert ist.
Ich werde das ganze noch genauer weiter Testen und suche gerade nach Möglichkeiten, die Fehlernachricht besser zu gestalten. Die Fehlermeldung beinhaltet die Auflistung vom Stack, welche ich mit der Hilfe der dbghelp.dll (SymLoadModuleEx, SymFromAddr, UnDecorateSymbolName, usw.) erstelle. Allerdings möchte ich ungern die .pdb Datei immer mit ausliefern. Es gibt sicher eine Möglichkeit, die Stack-Informationen zu speichern/exportieren und nachträglich mit einer .pdb Datei zu vervollständigen (Namen auflösen, usw.).
Wie verarbeitet ihr eure Fehler und wie zeigt ihr diese an?
Ich bin für jeden Tip sehr dankbar!