Exceptions und wie man in Vb.Net am Besten mit Ihnen umgeht (Code).

In einem früheren Beitrag habe ich dargelegt, das man eine Exception nur fangen sollte, wenn man sie wirklich beheben kann, alle anderen Exeptions sollte man an eine globale Fehlerbehandlung weiterreichen und hier ist nun der Teil mit dem dazugehörigen Vb.Net Code.

Im wesentlichen besteht der Code aus der Klasse cGeneralErrorHandler. Die Klasse muss in einem Modul definiert sein und wird dann in der Main()-Prozedur initialisiert.

1. Startformular umstellen

Normalerweise startet Vb.Net mit einem Formular. Da die Fehlermeldung aber global definiert werden muss, stellen wir den Startpunkt um. Dazu muss man in den Projekteigenschaften zunächst das Anwendungsframework deaktivieren

Bild der Projekteigenschaften unter Vb.Net, Unterpunkt Anwendung - Anwendungsframework aktivieren

Anschliessend muss man unter <Startobjekt> die Eigenschaft <Sub Main> auswählen.

Bild der Projekteigenschaften unter Vb.Net. Unterpunkt Anwendung - Startobjekt

2. Klasse definieren und initialisieren (mit Vb.Net Code)

Falls noch nicht geschehen müssen Sie jetzt ein neues Modul anlegen. Der Name spielt keine Rolle. Hier definieren Sie die Klasse und legen die Prozdur <Main> an.

Um Flexibel zu sein, übergebe ich bei der Initialisierung der Klasse einen Text (“Best Program ever”), der dann bei der angezeigten Messagebox als Titel bzw. Überschrift angezeigt wird und die Url der Fehlerprotokoll-Datei. In den meisten Fällen dürfte es am praktischsten sein, keinen Pfad anzugeben, sodaß die Datei in das Verzeichniss geschrieben wird, in dem sich auch die Exe-Datei des Programms befindet.

Module Module1

    'Globale Fehlerbehandlung-> wird hier im Modul initialisiert 
    Public gErrHandler As New cGeneralErrorHandler("Best Program ever", "FehlerLog.txt")

    Public Sub Main()

        'Start-Prozedur mit Definition einer globalen Fehlerbehandlungsroutine
        AddHandler Application.ThreadException, AddressOf gErrHandler.subGeneralErrorHandler

        'Formular, welches in Ihrer Anwendung als erstes angezeigt werden soll - Namen ggf. ändern
        Application.Run(New frmStartseite)

    End Sub

End Module

3. cGeneralErrorHandler (Vb.Net Code)

Hier nun der Code, der eigentlichen Klasse

''' <summary>
'''     Globale Fehlerbehandlung
''' </summary>
Public Class cGeneralErrorHandler

    Private sTitel_Messagebox As String
    Private sUrl_Logdatei As String

    Private _Ex As Exception = Nothing

    ''' <summary>
    '''     Globale Fehlerbehandlung initialisieren
    ''' </summary>
    ''' <param name="Titel_Messagebox">Titel_Messagebox - Titel der Messagebox, welche im Fehlerfall angezeigt wird</param>
    ''' <param name="Url_Logdatei">Url_Logdatei - kompletter Pfad mit Dateiname der Protokolldatei</param>
    Public Sub New(Titel_Messagebox As String, Url_Logdatei As String)

        sTitel_Messagebox = Titel_Messagebox
        sUrl_Logdatei = Url_Logdatei

    End Sub

    ''' <summary>
    '''     Diese Funktion muss im Startmodul "Sub Main()" als globale Fehlerbehandlungsroutine hinterlegt werden
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Public Sub subGeneralErrorHandler(ByVal sender As Object, ByVal e As System.Threading.ThreadExceptionEventArgs)
        Dim sErr1$, sErr2$, sErr3$, sErr4$, sErr5$, sWo$, iStartIndex%

        If _Ex Is Nothing Then                      ' -> Fehler wurde durch Programmfehler erzeugt
            sWo = e.Exception.StackTrace.ToString

            sErr1 = "Anwendungsfehler:     " & e.Exception.Message

            sErr2 = sWo.Substring(sWo.IndexOf("bei ") + 3, sWo.IndexOf("in ") - sWo.IndexOf("bei ") - 3)
            Dim index As Integer = sErr2.LastIndexOf(vbCrLf)
            If index = -1 Then
                sErr2 = "Source Function/Sub: " & sErr2.Substring(sErr2.IndexOf(".") + 1)
            Else
                sErr2 = "Source Function/Sub: " & sErr2.Substring(sErr2.LastIndexOf(vbCrLf) + 8)
            End If

            iStartIndex = sWo.IndexOf("in ")
            sErr3$ = "Source Line Number:   " & sWo.Substring(sWo.IndexOf("in ") + 3, sWo.IndexOf(vbCrLf, iStartIndex) - sWo.IndexOf("in "))
            sErr4 = ""
            MessageBox.Show("Obacht: Es ist etwas völlig unerwartetes passiert." & vbCrLf & vbCrLf & "Die Anwendung wird nun   GESCHLOSSEN   !", sTitel_Messagebox, MessageBoxButtons.OK, MessageBoxIcon.Error)
        Else                                        ' -> Fehler wurde mit "Throw New Exception(...)" erzeugt
            sWo = _Ex.StackTrace.ToString
            sErr1$ = "Nachricht:            " & e.Exception.Message.ToString
            sErr2$ = "Anwendungsfehler:     " & _Ex.Message

            sErr3$ = "Source Function/Sub:  " & sWo.Substring(sWo.LastIndexOf("bei ") + 4, sWo.IndexOf("in ") - sWo.LastIndexOf("bei ") - 4)
            sErr4$ = "Source Line Number:   " & sWo.Substring(sWo.IndexOf("in ") + 3)
            MessageBox.Show("Obacht: " & e.Exception.Message.ToString & vbCrLf & vbCrLf & "Die Anwendung wird nun   GESCHLOSSEN   !", sTitel_Messagebox, MessageBoxButtons.OK, MessageBoxIcon.Error)
        End If

        sErr5$ = "Das Programm wird nun   b e e n d e t !"

#If DEBUG Then
        'ausführliche Meldung  ->  nur während der Entwicklung aufrufen
        MessageBox.Show(sErr1 & vbCrLf & vbCrLf & sErr2 & vbCrLf & vbCrLf & sErr3 & vbCrLf & vbCrLf & sErr4 & vbCrLf & vbCrLf & sErr5, sTitel_Messagebox, MessageBoxButtons.OK, MessageBoxIcon.Error)
#Else

#End If

        '... auf Festplatte speichern...
        Dim FS1 As IO.StreamWriter = IO.File.AppendText(sUrl_Logdatei)
        FS1.WriteLine(Date.Now & vbCrLf & sErr1 & vbCrLf & sErr2 & vbCrLf & sErr3 & vbCrLf & sErr4 & vbCrLf)
        FS1.Close()

        ' ... und Anwendung beenden
        Application.Exit()

    End Sub

    'mit Hilfe von Ex kann in einem Catch-Zweig das Fehler-Objekt übergeben werden
    Public WriteOnly Property Ex As Exception
        Set(value As Exception)
            _Ex = value
        End Set
    End Property

End Class

4. Test

Ich habe zum Testen einfach zwei Buttons auf das Formular gezogen. Mit dem Einen teste ich einen “unerwarteten” Fehler, mit dem Anderen “fange” ich einen Fehler.

4.1. “unerwarteter” Fehler (mit Vb.Net Code)

Im Click-Event des Buttons provozieren wir eine DirectoryNotFoundException.

        Dim sUrl As String = "C:\Pfad\Zur\NichtExistierendenDatei.txt"
        Dim sContent As String = System.IO.File.ReadAllText(sUrl)

Dem Benutzer wird eine MessageBox angezeigt, die die in der Klasse hinterlegt Meldung anzeigt (“Obacht: Es ist etwas völlig unerwartetes passiert. Die Anwendung wird nun GESCHLOSSEN !”). Anschliesend wird die ausführliche Fehlermeldung in’s Fehlerlog geschrieben.

4.2. “Try .. Catch” (mit Vb.Net Code)

Dieses mal wird der Fehler in einem Try..Catch Block ausgelöst.

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Try
            ' Versuch, eine nicht existierende Datei zu öffnen'
            Dim sUrl As String = "C:\Pfad\Zur\NichtExistierendenDatei.txt"
            Dim sContent As String = System.IO.File.ReadAllText(sUrl)
        Catch ex As Exception
            'über diese Konstrukt erhalte ich in der globalen Fehlerbehandlung Zugriff auf die Original Fehlermeldung, die ich dann protokollieren kann
            gErrHandler.Ex = ex
            'mithilfe der Exception kann ich dem User in der globalen Fehlerbehandlung eine sinnvolle Fehlermeldung ausgeben
            Throw New Exception("Es kann momentan nicht auf Dateien zugegriffen werden.")
        End Try

    End Sub

Der User bekommt die Fehlermeldung angezeigt, die im Catch-Block hinterlegt ist (“Es kann momentan nicht auf Dateien zugegriffen werden.”). Da es sich um den gleichen Fehler wie unter 4.1. handelt, wird als “Anwendungsfehler” die gleich Fehlermeldung in’s Fehlerlog geschrieben. “Function/Sub” und “Line Number” ändern sich natürlich.

5. Fehlerlog

Das Fehlerlog sieht wiefolgt aus:

22.08.2023 08:22:05
Anwendungsfehler:     Ein Teil des Pfades "C:\Pfad\Zur\NichtExistierendenDatei.txt" konnte nicht gefunden werden.
Source Function/Sub:  MeinProjekt.frmStartseite.Button2_Click(Object sender, EventArgs e) 
Source Line Number:   R:\Workplace\aktuelle Projekte\MeinProjekt\frmStartseite.vb:Zeile 45.
 
22.08.2023 08:30:42
Nachricht:            Es kann momentan nicht auf Dateien zugegriffen werden.
Anwendungsfehler:     Ein Teil des Pfades "C:\Pfad\Zur\NichtExistierendenDatei.txt" konnte nicht gefunden werden.
Source Function/Sub:  MeinProjekt.frmStartseite.Button1_Click(Object sender, EventArgs e) 
Source Line Number:   R:\Workplace\aktuelle Projekte\MeinProjekt\frmStartseite.vb:Zeile 32.

6. Firmennetzwerk

In einem Firmennetzwerk, bzw. wenn mehrere User damit arbeiten, könnte man sich noch überlegen ob es sinnvoll ist die IT per E-Mail zu informieren. So könnte man im Fehlerfall schnell reagieren.

Man könnte auch ein Flag-File erstellen, das regelmässig serverseitig abgefragt wird und die Störrung dann meldet.

Oder man könnte im Fehlerprotokoll den Rechnernamen protokollieren. Dann kann man den User proaktiv informieren sobald der Fehler behoben ist.

Es gibt also noch einige offene Punkte…


Keine Kommentare (0)

Leave a Reply

Your email address will not be published. Required fields are marked *