Skip to content

Error Reporting & Exception Catching Refactor #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 73 additions & 79 deletions WinNUT_V2/WinNUT-Client/ApplicationEvents.vb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
'
' This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY

Imports System.Globalization
Imports System.IO
Imports System.Text.RegularExpressions
Imports Microsoft.VisualBasic.ApplicationServices
Imports Newtonsoft.Json
Imports WinNUT_Client_Common

Namespace My
Expand All @@ -19,23 +22,31 @@ Namespace My
' StartupNextInstance : Déclenché lors du lancement d'une application à instance unique et si cette application est déjà active.
' NetworkAvailabilityChanged : Déclenché quand la connexion réseau est connectée ou déconnectée.
Partial Friend Class MyApplication
' Default culture for output so logs can be shared with the project.
Private Shared DEF_CULTURE_INFO As CultureInfo = CultureInfo.InvariantCulture


Private CrashBug_Form As New Form
Private BtnClose As New Button
Private BtnGenerate As New Button
Private Msg_Crash As New Label
Private Msg_Error As New TextBox

Private crashReportData As String

Private Sub MyApplication_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
'Init WinNUT Variables
' Uncomment below and comment out Handles line for _UnhandledException sub when debugging unhandled exceptions.
' AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf AppDomainUnhandledException
Init_Globals()
LogFile.LogTracing("Init Globals Variables Complete", LogLvl.LOG_DEBUG, Me)
LogFile.LogTracing("MyApplication_Startup complete.", LogLvl.LOG_DEBUG, Me)
End Sub

Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
e.ExitApplication = False
Private Sub AppDomainUnhandledException(sender As Object, e As System.UnhandledExceptionEventArgs)
MyApplication_UnhandledException(sender, New UnhandledExceptionEventArgs(False, e.ExceptionObject))
End Sub

Dim Frms As New FormCollection
Frms = Application.OpenForms()
Private Sub MyApplication_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
e.ExitApplication = False

With Msg_Crash
.Location = New Point(6, 6)
Expand All @@ -49,19 +60,12 @@ Namespace My
.Size = New Point(470, 100)
End With

Dim Exception_data = BuildExceptionString(e.Exception)

If e.Exception.InnerException IsNot Nothing Then
Exception_data &= vbNewLine & "InnerException present:" & vbNewLine
Exception_data &= BuildExceptionString(e.Exception.InnerException)
End If

With Msg_Error
.Location = New Point(6, 110)
.Multiline = True
.ScrollBars = ScrollBars.Vertical
.ReadOnly = True
.Text = Exception_data.ToString()
.Text = e.Exception.ToString()
.Size = New Point(470, 300)
End With

Expand Down Expand Up @@ -93,99 +97,89 @@ Namespace My
.Controls.Add(BtnGenerate)
End With

crashReportData = GenerateCrashReport(e.Exception)

AddHandler BtnClose.Click, AddressOf Application.Close_Button_Click
AddHandler BtnGenerate.Click, AddressOf Application.Generate_Button_Click
AddHandler CrashBug_Form.FormClosing, AddressOf Application.CrashBug_FormClosing

CrashBug_Form.Show()
CrashBug_Form.BringToFront()
WinNUT.HasCrashed = True
End Sub

''' <summary>
''' Generate a friendly message describing an exception.
''' </summary>
''' <param name="ex">The exception that will be read for the message.</param>
''' <returns>The final string representation of the exception.</returns>
Private Function BuildExceptionString(ex As Exception) As String
Dim retStr = String.Empty

retStr &= String.Format("Exception type: {0}" & vbNewLine, ex.GetType.ToString)
retStr &= String.Format("Exception message: {0}" & vbNewLine, ex.Message)
retStr &= "Exception stack trace:" & vbNewLine
retStr &= ex.StackTrace & vbNewLine

Return retStr
End Function

Private Sub CrashBug_FormClosing(sender As Object, e As FormClosingEventArgs)
End
End Sub
Private Sub Close_Button_Click(sender As Object, e As EventArgs)
End
End Sub

Private Sub Generate_Button_Click(sender As Object, e As EventArgs)
'Generate a bug report with all essential datas
Dim Crash_Report As String = "WinNUT Bug Report" & vbNewLine
Dim WinNUT_Config As New Dictionary(Of String, Object)
Try
WinNUT_Config = Arr_Reg_Key
Catch ex As Exception
Crash_Report &= "ALERT: Encountered exception while trying to access Arr_Reg_Key:" & vbNewLine
Crash_Report &= BuildExceptionString(ex)
End Try

' Initialize directory for data
Dim CrashLog_Dir = ApplicationData & "\CrashLog"
If Not Computer.FileSystem.DirectoryExists(CrashLog_Dir) Then
Computer.FileSystem.CreateDirectory(CrashLog_Dir)
End If
Private Shared Function GenerateCrashReport(ex As Exception) As String
Dim jsonSerializerSettings As New JsonSerializerSettings()
jsonSerializerSettings.Culture = DEF_CULTURE_INFO
jsonSerializerSettings.Formatting = Formatting.Indented

Dim CrashLog_Filename As String = "Crash_Report_" & Format(Now, "dd-MM-yyyy") & "_" &
String.Format("{0}-{1}-{2}.txt", Now.Hour.ToString("00"), Now.Minute.ToString("00"), Now.Second.ToString("00"))
Dim reportStream As New StringWriter(DEF_CULTURE_INFO)
reportStream.WriteLine("WinNUT Bug Report")
reportStream.WriteLine("Generated at " + Date.UtcNow.ToString("F", DEF_CULTURE_INFO))
reportStream.WriteLine()
reportStream.WriteLine("OS Version: " & Computer.Info.OSVersion)
reportStream.WriteLine("WinNUT Version: " & ProgramVersion)

#Region "Config output"
Dim confCopy = New Dictionary(Of String, Object)

reportStream.WriteLine()
reportStream.WriteLine("==== Parameters ====")
reportStream.WriteLine()

Crash_Report &= "Os Version : " & Computer.Info.OSVersion & vbNewLine
Crash_Report &= "WinNUT Version : " & Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString & vbNewLine

Crash_Report &= vbNewLine & "WinNUT Parameters : " & vbNewLine
If WinNUT_Config.Count > 0 Then
' Prepare config values by removing sensitive information.
' Censor any identifying information
If Arr_Reg_Key IsNot Nothing AndAlso Arr_Reg_Key.Count > 0 Then
For Each kvp As KeyValuePair(Of String, Object) In Arr_Reg_Key
Dim newVal As String
Select Case kvp.Key
Case "ServerAddress", "Port", "UPSName", "NutLogin", "NutPassword"
WinNUT_Config.Remove(kvp.Key)
newVal = "*****"
Case Else
newVal = kvp.Value
End Select

confCopy.Add(kvp.Key, newVal)
Next
Crash_Report &= Newtonsoft.Json.JsonConvert.SerializeObject(WinNUT_Config, Newtonsoft.Json.Formatting.Indented) & vbNewLine

reportStream.WriteLine(JsonConvert.SerializeObject(confCopy, jsonSerializerSettings))
reportStream.WriteLine()
Else
Crash_Report &= "[EMPTY]" & vbNewLine
reportStream.WriteLine("[EMPTY]")
End If
#End Region

Crash_Report &= vbNewLine & "Error Message : " & vbNewLine
Crash_Report &= Msg_Error.Text & vbNewLine & vbNewLine
Crash_Report &= "Last Events :" & vbNewLine
#Region "Exceptions"
reportStream.WriteLine("==== Exception ====")
reportStream.WriteLine()
reportStream.WriteLine(Regex.Unescape(JsonConvert.SerializeObject(ex, jsonSerializerSettings)))
reportStream.WriteLine()
#End Region

For Each WinNUT_Event In LogFile.LastEvents
Crash_Report &= WinNUT_Event & vbNewLine
Next
reportStream.WriteLine("==== Last Events ====")

Computer.Clipboard.SetText(Crash_Report)
LogFile.LastEvents.Reverse()
reportStream.WriteLine()
reportStream.WriteLine(Regex.Unescape(JsonConvert.SerializeObject(LogFile.LastEvents, jsonSerializerSettings)))

Dim CrashLog_Report As StreamWriter
CrashLog_Report = Computer.FileSystem.OpenTextFileWriter(CrashLog_Dir & "\" & CrashLog_Filename, True)
CrashLog_Report.WriteLine(Crash_Report)
Return reportStream.ToString()
End Function

Private Sub Generate_Button_Click(sender As Object, e As EventArgs)
Dim logFileName = "CrashReport_" + Date.Now.ToString("s").Replace(":", ".") + ".txt"

Computer.Clipboard.SetText(crashReportData)

Directory.CreateDirectory(TEMP_DATA_PATH)
Dim CrashLog_Report = New StreamWriter(Path.Combine(TEMP_DATA_PATH, logFileName))
CrashLog_Report.WriteLine(crashReportData)
CrashLog_Report.Close()

' Open an Explorer window to the crash log.
' Dim fullFilepath As String = CrashLog_Dir & "\" & CrashLog_Filename
' If WinNUT IsNot Nothing Then
Process.Start(CrashLog_Dir)
' End If
Process.Start(TEMP_DATA_PATH)
End
End Sub

Private Sub Close_Button_Click(sender As Object, e As EventArgs)
CrashBug_Form.Close()
End Sub
End Class
End Namespace
4 changes: 2 additions & 2 deletions WinNUT_V2/WinNUT-Client/WinNUT-client.vbproj
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
<Version>7.1.2</Version>
<Version>7.1.3</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
<Version>13.0.3</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
Expand Down
32 changes: 14 additions & 18 deletions WinNUT_V2/WinNUT-Client/WinNUT.vb
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ Public Class WinNUT
Private Event RequestConnect()

Private Sub WinNUT_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Make sure we have an app directory to write to.
' SetupAppDirectory()

AddHandler Microsoft.Win32.SystemEvents.PowerModeChanged, AddressOf SystemEvents_PowerModeChanged
AddHandler RequestConnect, AddressOf UPS_Connect

'Add Main Gui's Strings
StrLog.Insert(AppResxStr.STR_MAIN_OLDINI_RENAMED, My.Resources.Frm_Main_Str_01)
StrLog.Insert(AppResxStr.STR_MAIN_OLDINI, My.Resources.Frm_Main_Str_02)
Expand Down Expand Up @@ -273,6 +267,9 @@ Public Class WinNUT
HasFocus = False
End If

AddHandler Microsoft.Win32.SystemEvents.PowerModeChanged, AddressOf SystemEvents_PowerModeChanged
AddHandler RequestConnect, AddressOf UPS_Connect

LogFile.LogTracing(String.Format("{0} v{1} completed initialization.", My.Application.Info.ProductName, My.Application.Info.Version),
LogLvl.LOG_NOTICE, Me)
End Sub
Expand Down Expand Up @@ -300,6 +297,7 @@ Public Class WinNUT
Arr_Reg_Key.Item("AutoReconnect"))

UPS_Device = New UPS_Device(Nut_Config, LogFile, Arr_Reg_Key.Item("Delay"))
AddHandler UPS_Device.EncounteredNUTException, AddressOf HandleNUTException
UPS_Device.Connect_UPS(retryOnConnFailure)
End Sub

Expand Down Expand Up @@ -334,15 +332,16 @@ Public Class WinNUT
End Sub

''' <summary>
''' Prepare application for and handle disconnecting from the UPS.
''' Prepare application for and initiate disconnecting from the UPS.
''' </summary>
Private Sub UPSDisconnect() ' Handles UPS_Device.Disconnected
' LogFile.LogTracing("Running Client disconnect subroutine.", LogLvl.LOG_DEBUG, Me)
Private Sub UPSDisconnect()
LogFile.LogTracing("Running Client disconnect subroutine.", LogLvl.LOG_DEBUG, Me)

If UPS_Device IsNot Nothing Then
UPS_Device.Disconnect(True)
RemoveHandler UPS_Device.EncounteredNUTException, AddressOf HandleNUTException
Else
LogFile.LogTracing("Attempted to disconnect when UPS_Device is Nothing.", LogLvl.LOG_DEBUG, Me)
LogFile.LogTracing("Attempted to disconnect when UPS_Device is Nothing.", LogLvl.LOG_ERROR, Me)
End If
End Sub

Expand Down Expand Up @@ -418,18 +417,14 @@ Public Class WinNUT
NotifyIcon.Visible = True
e.Cancel = True
Else
' TODO: Common shutdown subroutine? --v
LogFile.LogTracing("Init Disconnecting Before Close WinNut", LogLvl.LOG_DEBUG, Me)
RemoveHandler Microsoft.Win32.SystemEvents.PowerModeChanged, AddressOf SystemEvents_PowerModeChanged
UPSDisconnect()
LogFile.LogTracing("WinNut Is now Closed", LogLvl.LOG_DEBUG, Me)
End
End If
End Sub

Private Sub Menu_Quit_Click_1(sender As Object, e As EventArgs) Handles Menu_Quit.Click
LogFile.LogTracing("Close WinNut From Menu Quit", LogLvl.LOG_DEBUG, Me)
End
Application.Exit()
End Sub

Private Sub Menu_Settings_Click(sender As Object, e As EventArgs) Handles Menu_Settings.Click
Expand All @@ -442,7 +437,7 @@ Public Class WinNUT

Private Sub Menu_Sys_Exit_Click(sender As Object, e As EventArgs) Handles Menu_Sys_Exit.Click
LogFile.LogTracing("Close WinNut From Systray", LogLvl.LOG_DEBUG, Me)
End
Application.Exit()
End Sub

Private Sub Menu_Sys_Settings_Click(sender As Object, e As EventArgs) Handles Menu_Sys_Settings.Click
Expand Down Expand Up @@ -575,11 +570,12 @@ Public Class WinNUT
LogFile.LogTracing("Battery Status => " & Status, LogLvl.LOG_DEBUG, Me)
End Sub

Sub HandleNUTException(ex As NutException, sender As Object) Handles UPS_Device.EncounteredNUTException
Private Sub HandleNUTException(sender As UPS_Device, ex As NutException)
If ex.LastTransaction.ResponseType = NUTResponse.UNKNOWNUPS Then
Event_Unknown_UPS()
End If

LogFile.LogTracing("NUT protocol error encoutnered:" + vbNewLine + ex.ToString(), LogLvl.LOG_NOTICE, sender)
End Sub

Public Sub Event_Unknown_UPS() ' Handles UPS_Device.Unknown_UPS
Expand Down Expand Up @@ -837,7 +833,7 @@ Public Class WinNUT
' Setup logging preferences
If Arr_Reg_Key.Item("UseLogFile") Then
LogFile.LogLevelValue = Arr_Reg_Key.Item("Log Level")
LogFile.InitializeLogFile(ApplicationData)
LogFile.InitializeLogFile(ApplicationDataPath)
ElseIf LogFile.IsWritingToFile Then
LogFile.DeleteLogFile()
End If
Expand Down
Loading