From 7e79e98771a4f21314dbb0b1e536699261ae7dac Mon Sep 17 00:00:00 2001 From: MBucari Date: Wed, 16 Jul 2025 22:57:25 -0600 Subject: [PATCH] Fix possible cross-threading errors with MessageBoxBase --- Source/LibationAvalonia/MessageBox.cs | 6 ++--- .../ProcessQueue/ProcessBookViewModelBase.cs | 10 ++++++- Source/LibationWinForms/Program.cs | 27 ++++++++++++++++--- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Source/LibationAvalonia/MessageBox.cs b/Source/LibationAvalonia/MessageBox.cs index ae243330..290b254f 100644 --- a/Source/LibationAvalonia/MessageBox.cs +++ b/Source/LibationAvalonia/MessageBox.cs @@ -107,12 +107,12 @@ namespace LibationAvalonia } private static async Task ShowCoreAsync(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true) + => await Dispatcher.UIThread.InvokeAsync(async () => { owner = owner?.IsLoaded is true ? owner : null; - var dialog = await Dispatcher.UIThread.InvokeAsync(() => CreateMessageBox(owner, message, caption, buttons, icon, defaultButton, saveAndRestorePosition)); - + var dialog = CreateMessageBox(owner, message, caption, buttons, icon, defaultButton, saveAndRestorePosition); return await DisplayWindow(dialog, owner); - } + }); private static MessageBoxWindow CreateMessageBox(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true) { diff --git a/Source/LibationUiBase/ProcessQueue/ProcessBookViewModelBase.cs b/Source/LibationUiBase/ProcessQueue/ProcessBookViewModelBase.cs index ca794be9..62b00e7d 100644 --- a/Source/LibationUiBase/ProcessQueue/ProcessBookViewModelBase.cs +++ b/Source/LibationUiBase/ProcessQueue/ProcessBookViewModelBase.cs @@ -399,7 +399,15 @@ public abstract class ProcessBookViewModelBase : ReactiveObject const MessageBoxButtons SkipDialogButtons = MessageBoxButtons.AbortRetryIgnore; const MessageBoxDefaultButton SkipDialogDefaultButton = MessageBoxDefaultButton.Button1; - return await MessageBoxBase.Show(skipDialogText, "Skip this book?", SkipDialogButtons, MessageBoxIcon.Question, SkipDialogDefaultButton); + try + { + return await MessageBoxBase.Show(skipDialogText, "Skip this book?", SkipDialogButtons, MessageBoxIcon.Question, SkipDialogDefaultButton); + } + catch (Exception ex) + { + Serilog.Log.Logger.Error(ex, "Error showing retry dialog. Defaulting to 'Retry'; action."); + return DialogResult.Retry; + } } #endregion diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs index 01af7fa9..ba1416f0 100644 --- a/Source/LibationWinForms/Program.cs +++ b/Source/LibationWinForms/Program.cs @@ -23,9 +23,6 @@ namespace LibationWinForms { Task> libraryLoadTask; - LibationUiBase.Forms.MessageBoxBase.ShowAsyncImpl = (owner, message, caption, buttons, icon, defaultButton, saveAndRestorePosition) => - Task.FromResult((LibationUiBase.Forms.DialogResult)MessageBox.Show(owner as IWin32Window, message, caption, (MessageBoxButtons)buttons, (MessageBoxIcon)icon, (MessageBoxDefaultButton)defaultButton)); - try { //// Uncomment to see Console. Must be called before anything writes to Console. @@ -90,7 +87,31 @@ namespace LibationWinForms var form1 = new Form1(); form1.Load += async (_, _) => await form1.InitLibraryAsync(await libraryLoadTask); + LibationUiBase.Forms.MessageBoxBase.ShowAsyncImpl = ShowMessageBox; Application.Run(form1); + + #region Message Box Handler for LibationUiBase + Task ShowMessageBox( + object owner, + string message, + string caption, + LibationUiBase.Forms.MessageBoxButtons buttons, + LibationUiBase.Forms.MessageBoxIcon icon, + LibationUiBase.Forms.MessageBoxDefaultButton defaultButton, + bool _) + { + var result = form1.Invoke(() => + MessageBox.Show( + owner as IWin32Window ?? form1, + message, + caption, + (MessageBoxButtons)buttons, + (MessageBoxIcon)icon, + (MessageBoxDefaultButton)defaultButton)); + + return Task.FromResult((LibationUiBase.Forms.DialogResult)result); + } + #endregion; } private static void RunInstaller(Configuration config)