Global exception handling. Threadsafe MessageBoxAlertAdminDialog

This commit is contained in:
Robert McRackan 2022-05-27 13:38:28 -04:00
parent 5a80a0cc06
commit 114925ebce
9 changed files with 56 additions and 20 deletions

View File

@ -256,18 +256,21 @@ namespace AppScaffolding
private static void logStartupState(Configuration config)
{
#if DEBUG
var mode = "Debug";
#else
var mode = "Release";
#endif
if (System.Diagnostics.Debugger.IsAttached)
mode += " (Debugger attached)";
// begin logging session with a form feed
Log.Logger.Information("\r\n\f");
Log.Logger.Information("Begin. {@DebugInfo}", new
{
AppName = EntryAssembly.GetName().Name,
Version = BuildVersion.ToString(),
#if DEBUG
Mode = "Debug",
#else
Mode = "Release",
#endif
Mode = mode,
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
LogLevel_Information_Enabled = Log.Logger.IsInformationEnabled(),

View File

@ -124,7 +124,7 @@ namespace LibationWinForms.Dialogs
}
catch (Exception ex)
{
MessageBoxLib.ShowAdminAlert("Error attempting to save accounts", "Error saving accounts", ex);
MessageBoxLib.ShowAdminAlert(this, "Error attempting to save accounts", "Error saving accounts", ex);
}
}

View File

@ -46,7 +46,7 @@ namespace LibationWinForms.Dialogs
if (template is null)
{
MessageBoxLib.ShowAdminAlert($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null"));
MessageBoxLib.ShowAdminAlert(this, $"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null"));
return;
}

View File

@ -77,6 +77,7 @@ namespace LibationWinForms.Dialogs
catch (Exception ex)
{
MessageBoxLib.ShowAdminAlert(
this,
"Error scanning library. You may still manually select books to remove from Libation's library.",
"Error scanning library",
ex);

View File

@ -40,7 +40,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxLib.ShowAdminAlert("Error attempting to export your library.", "Error exporting", ex);
MessageBoxLib.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex);
}
}
}

View File

@ -126,6 +126,7 @@ namespace LibationWinForms
catch (Exception ex)
{
MessageBoxLib.ShowAdminAlert(
this,
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
"Error importing library",
ex);

View File

@ -4,22 +4,27 @@ using System.Linq;
using System.Windows.Forms;
using DataLayer;
using Dinah.Core.Logging;
using Dinah.Core.Threading;
using LibationWinForms.Dialogs;
using Serilog;
namespace LibationWinForms
{
public static class MessageBoxLib
public static class MessageBoxLib
{
/// <summary>
/// Logs error. Displays a message box dialog with specified text and caption.
/// </summary>
/// <param name="synchronizeInvoke">Form calling this method.</param>
/// <param name="text">The text to display in the message box.</param>
/// <param name="caption">The text to display in the title bar of the message box.</param>
/// <param name="exception">Exception to log</param>
/// <returns>One of the System.Windows.Forms.DialogResult values.</returns>
public static DialogResult ShowAdminAlert(string text, string caption, Exception exception)
/// <param name="exception">Exception to log.</param>
public static void ShowAdminAlert(System.ComponentModel.ISynchronizeInvoke owner, string text, string caption, Exception exception)
{
// for development and debugging, show me what broke!
if (System.Diagnostics.Debugger.IsAttached)
throw exception;
try
{
Serilog.Log.Logger.Error(exception, "Alert admin error: {@DebugText}", new { text, caption });
@ -27,10 +32,22 @@ namespace LibationWinForms
catch { }
using var form = new MessageBoxAlertAdminDialog(text, caption, exception);
return form.ShowDialog();
if (owner is not null)
{
try
{
owner.UIThreadSync(() => form.ShowDialog());
return;
}
catch { }
}
// synchronizeInvoke is null or previous attempt failed. final try
form.ShowDialog();
}
public static void VerboseLoggingWarning_ShowIfTrue()
public static void VerboseLoggingWarning_ShowIfTrue()
{
// when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured
if (Log.Logger.IsVerboseEnabled())

View File

@ -49,7 +49,7 @@ namespace LibationWinForms
#if !DEBUG
checkForUpdate();
#endif
// logging is init'd here
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config);
}
catch (Exception ex)
@ -58,14 +58,17 @@ namespace LibationWinForms
var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file.";
try
{
MessageBoxLib.ShowAdminAlert(body, title, ex);
MessageBoxLib.ShowAdminAlert(null, body, title, ex);
}
catch
{
MessageBox.Show($"{body}\r\n\r\n{ex.Message}\r\n\r\n{ex.StackTrace}", title, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return;
}
}
// global exception handling (ShowAdminAlert) attempts to use logging. only call it after logging has been init'd
postLoggingGlobalExceptionHandling();
Application.Run(new Form1());
}
@ -170,7 +173,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxLib.ShowAdminAlert("Error checking for update", "Error checking for update", ex);
MessageBoxLib.ShowAdminAlert(null, "Error checking for update", "Error checking for update", ex);
return;
}
@ -182,5 +185,16 @@ namespace LibationWinForms
Updater.Run(upgradeProperties.LatestRelease, upgradeProperties.ZipUrl);
}
private static void postLoggingGlobalExceptionHandling()
{
// this line is all that's needed for strict handling
AppDomain.CurrentDomain.UnhandledException += (_, e) => MessageBoxLib.ShowAdminAlert(null, "Libation has crashed due to an unhandled error.", "Application crash!", (Exception)e.ExceptionObject);
// these 2 lines makes it graceful. sync (eg in main form's ctor) and thread exceptions will still crash us, but event (sync, void async, Task async) will not
Application.ThreadException += (_, e) => MessageBoxLib.ShowAdminAlert(null, "Libation has encountered an unexpected error.", "Unexpected error", e.Exception);
// I never found a case where including made a difference. I think this enum is default and including it will override app user config file
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
}
}
}

View File

@ -43,7 +43,7 @@ namespace LibationWinForms
}
catch (Exception ex)
{
MessageBoxLib.ShowAdminAlert("Error downloading update", "Error downloading update", ex);
MessageBoxLib.ShowAdminAlert(null, "Error downloading update", "Error downloading update", ex);
}
}
}