UnhandledExceptions in einer Klassenbibliothek

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 31 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    UnhandledExceptions in einer Klassenbibliothek

    Moin zusammen,

    ich habe ein AddIn für eine vorhandene Software geschrieben, welches in Form einer Klassenbibliothek (dll) vorliegt und so durch die vorhandene Software beim StartUp geladen wird.
    Dank dem @ErfinderDesRades und seinen Hilfestellungen zum Thema XML und seinen sonstigen Tutorials zum Thema typisiertes Dataset funktioniert auch alles wie geplant.

    In der vorhandenen Software kann ich nun über Ribbon-Schaltflächen die Funktionalitäten des AddIns (dll) aufrufen. Die Funktionalitäten bestehen alle aus WinForms, welche beim klick auf die Ribbon-Schaltflächen geöffnet werden.

    Im "normalen" Betrieb werden eigentlich keine Exceptions geworfen, bzw. fange ich alle bereits bekannten Exceptions und behandle sie entsprechend.
    Dennoch kommt es immer mal wieder zu Situationen, bzw. Konstellationen in denen UnhandledExceptions geworfen werden. Problem hierbei ist, das der Endanwender diese in der Regel einfach wegklickt und somit das Feedback nicht bei mir ankommt.

    Aus diesem Grund würde ich die UnhandledExceptions gerne protokollieren.
    Das Problem hierbei ist, dass ich nicht weis wie :(

    Nach ausgiebiger Recherche bin ich bereits auf sowas gestoßen:

    VB.NET-Quellcode

    1. Public Event UnhandledException As UnhandledExceptionEventHandler

    In Kombination mit sowas:

    VB.NET-Quellcode

    1. Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
    2. ' Tue hier etwas mit der UnhandledException, z.B. protokollieren
    3. End Sub


    Problem ist, das er auch wenn eine UnhandledException geworfen wird, nicht den Sub durchläuft und somit auch nicht protokolliert.

    Eventuell platziere ich den Code auch einfach nur an der falschen Stelle?
    Ich habe auch schon mal gelesen, dass diese Art von ExceptionHandling nur in reinen WinFormsAnwendungen möglich ist...

    Allerdings haben meine Tests, bei denen ich dieses Prozedere in einer WinFormsAnwendung ausprobiert habe auch nicht dazu geführt, das der Sub aufgerufen wird.

    VB.NET-Quellcode

    1. Imports Microsoft.VisualBasic.ApplicationServices
    2. Namespace My
    3. ' Für MyApplication sind folgende Ereignisse verfügbar:
    4. ' Startup: Wird beim Starten der Anwendung noch vor dem Erstellen des Startformulars ausgelöst.
    5. ' Shutdown: Wird nach dem Schließen aller Anwendungsformulare ausgelöst. Dieses Ereignis wird nicht ausgelöst, wenn die Anwendung mit einem Fehler beendet wird.
    6. ' UnhandledException: Wird bei einem Ausnahmefehler ausgelöst.
    7. ' StartupNextInstance: Wird beim Starten einer Einzelinstanzanwendung ausgelöst, wenn die Anwendung bereits aktiv ist.
    8. ' NetworkAvailabilityChanged: Wird beim Herstellen oder Trennen der Netzwerkverbindung ausgelöst.
    9. Partial Friend Class MyApplication
    10. Private Sub MyApplication_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
    11. ' Tue hier etwas mit der UnhandledException, z.B. protokollieren
    12. End Sub
    13. End Class
    14. End Namespace


    Ich habe in der WinFormsAnwendung mit Option Strict Off eine InvalidCastException und eine StackOverflowException ausgelöst und in beiden Fällen hat es den Sub MyApplication_UnhandledException nicht durchlaufen.

    Nun meine Frage, wie gestalte ich die UnhandledException Protokollierung richtig und ist dies in einer Klassenbibliothek überhaupt möglich?

    Ruzbacky schrieb:

    ich habe ein AddIn für eine vorhandene Software geschrieben, welches in Form einer Klassenbibliothek (dll) vorliegt und so durch die vorhandene Software beim StartUp geladen wird.
    Wenn Du die Quellen der DLL hast, füge das DLL-Projekt Deiner GUI-Projektmappe als vorhandenes Projekt hinzu und füge als Verweis das DLL-Projekt, nicht aber die DLL Deinem GUI-Projekt als Verweis hinzu.
    Nimm die Try / Catches raus und lass es knallen, da steht das Programm unmittelbar in der Fehlerzeile, so dass Du sie korrigieren kannst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Wenn Du die Quellen der DLL hast, füge das DLL-Projekt Deiner GUI-Projektmappe als vorhandenes Projekt hinzu und füge als Verweis das DLL-Projekt, nicht aber die DLL Deinem GUI-Projekt als Verweis hinzu.

    Ich nehme an, dass du mit der GUI-Projektmappe die Projektmappe der von mir angesprochenen "vorhanden Software" meinst und ich in dieser mein DLL-Projekt (also das Projekt meines AddIns) als Verweis hinzufügen soll.
    Wenn das deine Idee war, dann habe ich mich in meinem Beitrag etwas unklar ausgedrückt... Die vorhandene Software ist nicht von mir, sondern von einem Drittanbieter. Somit verfüge ich über kein GUI-Projekt dieser Software.
    Die vorhandene Software ist lediglich in der Lage Zusatzmodule in Form von Klassenbibliotheken zu laden. Und genau so eine Klassenbibliothek habe ich erstellt.
    In dieser Klassenbibliothek ist dann mein komplettes AddIn enthalten, mit allen Forms, DataSets, Klassen, Modulen, usw.

    Bei der vorhandenen Software handelt es sich übrigens um "Autodesk Inventor", wobei dies wahrscheinlich irrelevant für die Lösung meines Problems ist.

    RodFromGermany schrieb:

    Nimm die Try / Catches raus und lass es knallen, da steht das Programm unmittelbar in der Fehlerzeile, so dass Du sie korrigieren kannst.

    Try/Catch verwende ich im Prinzip gar nicht, da ich alle mir bereits bekannten Fehlerquellen bereits durch entsprechende Gegenmaßnahmen behandle.

    Aus diesem Grund möchte ich ja die mir noch unbekannten Fehler protokollieren, damit ich diese dann auch noch entsprechend behandeln kann.

    Sollte ich dich völlig falsch verstanden haben, dann korrigiere mich bitte :)

    Ruzbacky schrieb:

    Die vorhandene Software ist nicht von mir, sondern von einem Drittanbieter.
    OK, zurück zu Feld Nummer 1.
    Wie äußern sich die UnhandledExceptions, die Du nicht abfangen kannst?
    Ich weiß, dass dieser Handler praktisch nur managed Exceptions auffängt.
    Falls in dieser DLL unmanaged C / C++-Exceptions geworfen werden, hast Du tatsächlich ein Problem, das sich aus der Ferne wohl nicht lösen lässt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Wie äußern sich die UnhandledExceptions, die Du nicht abfangen kannst?

    Die Exceptions äußern sich als normale unbehandelte Ausnahme.
    Und diese Informationen sind es, die ich eigentlich auch versuche zu protokollieren.

    RodFromGermany schrieb:

    Falls in dieser DLL unmanaged C / C++-Exceptions geworfen werden

    ​Die komplette Klassenbibliothek basiert auf vb.net, es sind keine Bestandteile mit C oder C++ -Code enthalten.

    Somit sollte es sich bei allen Exceptions um managaged Exceptions handeln oder sehe ich das falsch?
    Bilder
    • Exception.JPG

      50,95 kB, 427×334, 164 mal angesehen
    UnhandledException - diesen Datentyp kenne ich ühaupt nicht!

    Ansonsten fund ich hier sowas:

    VB.NET-Quellcode

    1. Imports Microsoft.VisualBasic.ApplicationServices
    2. Imports WinFormApp = System.Windows.Forms.Application
    3. Imports Microsoft.VisualBasic.ControlChars
    4. Namespace My
    5. Partial Friend Class MyApplication
    6. Private Sub App_Startup(ByVal sender As Object, ByVal e As StartupEventArgs) Handles Me.Startup
    7. AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf SideThread_Exception
    8. AddHandler WinFormApp.ThreadException, AddressOf MainThread_Exception
    9. End Sub
    10. Private Shared Sub MainThread_Exception(ByVal sender As Object, ByVal e As System.Threading.ThreadExceptionEventArgs)
    11. MessageBox.Show("Log unhandled main-thread-Exception here" & Lf & e.Exception.ToString())
    12. WinFormApp.Exit()
    13. End Sub
    14. Private Shared Sub SideThread_Exception(ByVal sender As Object, ByVal e As System.UnhandledExceptionEventArgs)
    15. MessageBox.Show("Log unhandled side-thread-Exception here" & Lf + e.ExceptionObject.ToString())
    16. WinFormApp.Exit()
    17. End Sub
    18. Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
    19. MessageBox.Show("Log MyApplication_UnhandledException-Exception here" & Lf + e.Exception.ToString())
    20. WinFormApp.Exit()
    21. End Sub
    22. End Class
    23. End Namespace
    Also es scheint 3 Mechanismen zu geben, um anwendungsweit sämtliche Exceptions einzusammeln. Warum 3, und wann welcher greift (oder ob etwas davon auch garnet funzt) weiss ich auch nicht.

    Ruzbacky schrieb:

    Die komplette Klassenbibliothek basiert auf vb.net, es sind keine Bestandteile mit C oder C++ -Code enthalten.
    Wie kommst Du zu dieser Aussage?
    Das ist eine COM-Exception, das ist mit hoher Wahrscheinlichkeit C++-Code, für den das Studio automatisch einen .NET-Wrapper generiert hat (weil es COM ist).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Das ist eine COM-Exception, das ist mit hoher Wahrscheinlichkeit C++-Code, für den das Studio automatisch einen .NET-Wrapper generiert hat (weil es COM ist).

    Da hast du natürlich recht, da war ich etwas auf dem falschen Dampfer... Da mein Code die Exception wirft, war ich auch am glauben, dass mein vb.net Code eine managed Exception wirft.
    So gesehen ist dort mit hoher Wahrscheinlichkeit massenhaft C++ Code enthalten.

    ErfinderDesRades schrieb:

    Ansonsten fund ich hier sowas:

    Dein Code funktioniert auf jeden Fall schonmal in einer WinFormAnwendung und zeigt warum mein Code aus dem Start-Beitrag nicht den gewünschten Effekt hatte.

    Produziere ich z.B. eine OverflowException beim Überlauf eines Integer, so ist das keine UnhandledException wie ich mir das gedacht habe, sondern eine SideThread-Exception.
    Dann zeigt er mir auch die MessageBox mit allen Informationen.

    Ist natürlich die Frage, ob ich dies auf eine Klassenbibliothek übertragen kann...
    Bilder
    • Exception.JPG

      106,72 kB, 413×719, 129 mal angesehen
    @Ruzbacky Wenn Du um die crashende Zeile ein Try / Catch baust und die Exception wird trotzdem nicht abgefangen, ist das das, was ich meine, da solltest Du Dich mal vertrauensvoll an den Service / Kundendienst des Herstellers wenden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Wenn Du um die crashende Zeile ein Try / Catch baust und die Exception wird trotzdem nicht abgefangen

    Wenn ich ein Try/Catch um die Zeile drum herum baue, dann fängt er die Exception ohne Probleme. Im Bild die MessageBox mit der gefangenen Exception.

    ​Also fangen kann ich alle Exceptions mit Try/Catch, da gibt es keine Probleme, nur ohne Try/Catch die Exception global zu fangen und zu protokollieren funktioniert noch überhaupt nicht.
    Bilder
    • Exception.JPG

      31,96 kB, 411×238, 128 mal angesehen
    @Ruzbacky OK.
    Das ist wohl wieder so ein VisualBasic-Namespace-Problem.
    Soll das Programm nach so einer Exception weiterlaufen oder nicht?
    Wenn nein, solltest Du das Framework Deiner Applikation abschalten und auf den bei C# üblichen Aufbau umstellen (Program.cs).
    Wenn ja, weiß ich nicht, ob das der VisualBasic-Namespace ühaupt macht, da solltest Du diese Exceptions lokal auffangen und einer zentralen Erfassungsroutine zuführen, die die protokolliert, dann kannst Du das Programm wie auch immer weiter ausführen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Soll das Programm nach so einer Exception weiterlaufen oder nicht?

    Die eigentliche Software soll weiter laufen, das AddIn, welches ja die Exception verursacht hat, soll beim auftreten der Exception nicht weiter laufen, da die Exceptions ja unbehandelt sind.

    Mit dem Code vom EdR habe ich noch mal etwas ausgiebiger eine WinFormsAnwendung getstet, hier werden alle Exceptions in einer der drei Mechanismen registriert und eine Protokollierung wäre ohne Probleme möglich.
    Aber wie es scheint, stehen diese Funktionalitäten ausschließlich für WinFormsAnwendungen zur Verfügung und somit nicht für Klassenbibliotheken.

    Hier noch ein Link, wo auch geschrieben wird, dass dies so nur für WinFormsAnwenungen möglich ist:
    msdn.microsoft.com/de-de/libra…dexception(v=vs.110).aspx
    Damit ich auch noch schnell die Übersicht verliere:
    Hauptbenutzerprogramm = Fremdsoftware (Autodesk Inventor)
    eigenes Programm = VB.Net DLL als AddIn für's Hauptprogramm, welches an irgendeiner Stelle crasht
    Wenn Dein AddIn crasht, dann läuft das Hauptprogramm weiter?
    Hm ... dann ginge als "globale Fehlerbehandlung" m.E. nur ein großer Try-Catch-Block, der den kompletten AddIn-Ablauf umfasst. Ggf. anfänglich mit ein paar Debugvariablen, die zumindest im Catch-Bereich bescheid geben, in welchem Modul, welcher Zeile es crashte. Und im Catch-Block sollte z.B. eine Datei oder eine eMail erstellt (und letztere an Dich geschickt) werden, damit Du davon was erfährst. Mein Vorschlag. Alternativen?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Ruzbacky schrieb:

    Aber wie es scheint, stehen diese Funktionalitäten ausschließlich für WinFormsAnwendungen zur Verfügung und somit nicht für Klassenbibliotheken.
    Hmm - also System.AppDomain - sagt mein ObjectBrowser - ist Bestandteil von mscorlib, und ohne mscorlib gibts ühaupt kein .Net-Programm.

    Ruzbacky schrieb:

    und somit nicht für Klassenbibliotheken
    Kann ich mir nicht vorstellen.
    Wenn ich jetzt solch zu programmieren hätte, würde ich jeden Zugriff auf diese Bibliothek mit Try / Catch umschließen und bei einer unhandled Exception das Hauptfenster per Event informieren, dass
    • eine Meldung kommt
    • eine Log-Info abgelegt wird
    • die DLL-Klasse(n) neu instanziiert wird/werden
    • mit den (noch) vorhandenen Daten die GUI korrekt dargestellt wird.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @VaporiZed Das passt soweit, allerdings war es ja mein Ziel genau so einen großen alles einschließenden Try/Catch Block zu vermeiden...

    ErfinderDesRades schrieb:

    Hmm - also System.AppDomain - sagt mein ObjectBrowser - ist Bestandteil von mscorlib, und ohne mscorlib gibts ühaupt kein .Net-Programm.

    ​Ich glaube trotzdem, dass ich die UnhandledException nur in einer WinFormAnwendung als Event behandeln kann, sprich nicht meine Klassenbibliothek müsste die UnhandledException fangen und protokollieren, sondern die darüber stehende Anwendung, in meinem Fall also Autodesk Inventor.

    RodFromGermany schrieb:

    Wenn ich jetzt solch zu programmieren hätte, würde ich jeden Zugriff auf diese Bibliothek mit Try / Catch umschließen und bei einer unhandled Exception das Hauptfenster per Event informieren

    ​Allerdings greife ich mit meinem Code ja nicht auf die DLL zu, sondern ich befinde mich in der DLL selbst. Der Zugriff erfolgt ja dann durch Autodesk Inventor, dann müsste Autodesk Inventor theoretisch die UnhandledException in meiner DLL fangen und protokollieren oder sehe ich das falsch?
    So ein Nonsens. Natürlich kann ich auch in einer Klassenbibliothek ein Event aus einem AppDomain-Objekt abonnieren um darauf zu reagieren.
    So verwende ich regelmäßig das Assembly-Resolve-Ereignis, um in AnyCPU-DLLs, die native Bibliotheken per P-Invoke aufrufen, abhängig von der aktuellen Prozessumgebung die 32bit- oder 64bit-Version der nativen DLL dynamisch zu laden.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.

    Ruzbacky schrieb:

    oder sehe ich das falsch?
    Du bist in der DLL und sagst, Autodesk soll die Exception abfangen.
    Dann tue in der DLL einfach nichts, da kommt die Exception dort an.
    Ich denke aber nicht, dass das das ist, was Du willst.
    Je näher am Ursprungsort eine Exception abgefangen wird, um so präziser kann man darauf reagieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    ErfinderDesRades schrieb:

    Du kannst natürlich glauben was du willst - ich aber an deiner Stelle würde es ausprobieren

    Ich habe es ausgiebig versucht, bekomme aber nicht das gewünschte Ergebnis, bzw. das Ergebnis wie ich es bei einer WinFormAnwendung bekomme...

    VB.NET-Quellcode

    1. Private Sub AppStartUp()
    2. AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf SideThread_Exception
    3. AddHandler Windows.Forms.Application.ThreadException, AddressOf MainThread_Exception
    4. End Sub
    5. Private Shared Sub SideThread_Exception(ByVal sender As Object, ByVal e As System.UnhandledExceptionEventArgs)
    6. MessageBox.Show("Log unhandled side-thread-Exception here" + e.ExceptionObject.ToString())
    7. End Sub
    8. Private Shared Sub MainThread_Exception(ByVal sender As Object, ByVal e As System.Threading.ThreadExceptionEventArgs)
    9. MessageBox.Show("Log unhandled main-thread-Exception here" & e.Exception.ToString())
    10. End Sub

    Den Sub "AppStartUp" rufe ich im Load-Event der Form auf, aber nur in einer WinFormAnwendung ruft er dann auch die Sub "SideThread_Exception" auf, wenn eine Exception auftritt. In meiner Dll wirft er mir die Exception einfach auf den Bildschirm und beendet danach das Form, ohne den Sub "SideThread-Exception" zu durchlaufen.

    RodFromGermany schrieb:

    Ich denke aber nicht, dass das das ist, was Du willst.

    Nein, natürlich will ich das nicht... Mein Verständnis war nur so, dass ich dachte die Anwendung, welche die DLL einbindet kann die UnhandledException fangen und behandeln, nicht aber die DLL selbst. Eben genau weil die DLL die Exception nach oben weiterreicht an die eigentliche Anwendung.
    Ich möchte auch keinesfalls sagen, dass dies so ist wie ich es beschreibe... ich schreibe nur wie ich es mir vorstelle, ob dies wirklich so ist vermag ich nicht zu beurteilen, dafür beherrsche ich die Materie weis Gott zu viel zu wenig.

    Arby schrieb:

    Natürlich kann ich auch in einer Klassenbibliothek ein Event aus einem AppDomain-Objekt abonnieren um darauf zu reagieren.

    ​Ja das geht natürlich, nur das UnhandledException Event will bei mir in einer DLL nicht laufen, fast so als würde die Exception zunächst an die eigentliche Hauptanwendung gereicht werden, bevor das Event auslösen kann.


    ​Bin nun zunächst doch vorerst auf Try/Catch Blöcke gegangen, wobei im Catch-Block dann das Prozedere zum protokollieren der Exception abgearbeitet wird.
    Was nicht heißen soll, das ich nicht an einer anderen eleganten Lösung interessiert bin. Gerne auch mit Verweisen, welche mein Wissen erweitern.