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
Anschliessend muss man unter <Startobjekt> die Eigenschaft <Sub Main> auswählen.
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…
Leave a Reply