diff --git a/Source/AppScaffolding/LibationScaffolding.cs b/Source/AppScaffolding/LibationScaffolding.cs index 251a05e4..10732bcd 100644 --- a/Source/AppScaffolding/LibationScaffolding.cs +++ b/Source/AppScaffolding/LibationScaffolding.cs @@ -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(), diff --git a/Source/LibationWinForms/Dialogs/AccountsDialog.cs b/Source/LibationWinForms/Dialogs/AccountsDialog.cs index 944516f9..e2a90e2a 100644 --- a/Source/LibationWinForms/Dialogs/AccountsDialog.cs +++ b/Source/LibationWinForms/Dialogs/AccountsDialog.cs @@ -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); } } diff --git a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs index 693072f7..3848c2c1 100644 --- a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs +++ b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs @@ -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; } diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs index 558095f0..84e5ff51 100644 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -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); diff --git a/Source/LibationWinForms/Form1.Export.cs b/Source/LibationWinForms/Form1.Export.cs index ae538cd2..4451a7c6 100644 --- a/Source/LibationWinForms/Form1.Export.cs +++ b/Source/LibationWinForms/Form1.Export.cs @@ -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); } } } diff --git a/Source/LibationWinForms/Form1.ScanManual.cs b/Source/LibationWinForms/Form1.ScanManual.cs index 1ba25b6f..4b9215d9 100644 --- a/Source/LibationWinForms/Form1.ScanManual.cs +++ b/Source/LibationWinForms/Form1.ScanManual.cs @@ -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); diff --git a/Source/LibationWinForms/MessageBoxLib.cs b/Source/LibationWinForms/MessageBoxLib.cs index 926cf747..040a9293 100644 --- a/Source/LibationWinForms/MessageBoxLib.cs +++ b/Source/LibationWinForms/MessageBoxLib.cs @@ -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 { /// /// Logs error. Displays a message box dialog with specified text and caption. /// + /// Form calling this method. /// The text to display in the message box. /// The text to display in the title bar of the message box. - /// Exception to log - /// One of the System.Windows.Forms.DialogResult values. - public static DialogResult ShowAdminAlert(string text, string caption, Exception exception) + /// Exception to log. + 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()) diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs index 1263f2a3..ecf2344a 100644 --- a/Source/LibationWinForms/Program.cs +++ b/Source/LibationWinForms/Program.cs @@ -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); + } } } diff --git a/Source/LibationWinForms/Updater.cs b/Source/LibationWinForms/Updater.cs index 032ed07b..1506f924 100644 --- a/Source/LibationWinForms/Updater.cs +++ b/Source/LibationWinForms/Updater.cs @@ -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); } } }