diff --git a/OpenNest.WinForms.Tests/ActionSelectAreaCutOffTests.cs b/OpenNest.WinForms.Tests/ActionSelectAreaCutOffTests.cs index ba9010e..f18c5b9 100644 --- a/OpenNest.WinForms.Tests/ActionSelectAreaCutOffTests.cs +++ b/OpenNest.WinForms.Tests/ActionSelectAreaCutOffTests.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.Actions; using OpenNest.Controls; @@ -86,19 +85,6 @@ public class ActionSelectAreaCutOffTests public void ToggleSelectionDirection() => OnKeyUp(new KeyEventArgs(Keys.Space)); } - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { failure = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(60)), "The selection/fill test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(60), "The selection/fill test did not complete."); } diff --git a/OpenNest.WinForms.Tests/ArchUnitsTests.cs b/OpenNest.WinForms.Tests/ArchUnitsTests.cs index d2068f5..d05cfb8 100644 --- a/OpenNest.WinForms.Tests/ArchUnitsTests.cs +++ b/OpenNest.WinForms.Tests/ArchUnitsTests.cs @@ -1,7 +1,6 @@ using System.Drawing; using System.Globalization; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Text.RegularExpressions; using System.Windows.Forms; @@ -48,19 +47,6 @@ public class ArchUnitsTests // Actual timeout execution is covered by the shared Regex mechanism in FractionTests; // these desktop tests deliberately avoid timing-sensitive oversized-input fixtures. - internal static void RunSta(Action action) - { - Exception? error = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { error = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(15)), "The STA test did not complete."); - if (error != null) - ExceptionDispatchInfo.Capture(error).Throw(); - } + internal static void RunSta(Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(15), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Controls/ProgramEditorControlTests.cs b/OpenNest.WinForms.Tests/Controls/ProgramEditorControlTests.cs index 56d91bc..609b66e 100644 --- a/OpenNest.WinForms.Tests/Controls/ProgramEditorControlTests.cs +++ b/OpenNest.WinForms.Tests/Controls/ProgramEditorControlTests.cs @@ -1,6 +1,5 @@ using System.Drawing; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Text.RegularExpressions; using System.Windows.Forms; using OpenNest.CNC; @@ -292,19 +291,6 @@ public class ProgramEditorControlTests private static void ApplyHighlighting(ProgramEditorControl control) => typeof(ProgramEditorControl) .GetMethod("ApplyHighlighting", BindingFlags.Instance | BindingFlags.NonPublic)!.Invoke(control, null); - private static void RunSta(Action action) - { - Exception? error = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { error = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(30)), "The STA test did not complete."); - if (error != null) - ExceptionDispatchInfo.Capture(error).Throw(); - } + private static void RunSta(Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(30), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Controls/SeparatorPenLifetimeTests.cs b/OpenNest.WinForms.Tests/Controls/SeparatorPenLifetimeTests.cs index b5ea976..e65f700 100644 --- a/OpenNest.WinForms.Tests/Controls/SeparatorPenLifetimeTests.cs +++ b/OpenNest.WinForms.Tests/Controls/SeparatorPenLifetimeTests.cs @@ -1,6 +1,5 @@ using System.Drawing; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.Controls; @@ -109,19 +108,6 @@ public class SeparatorPenLifetimeTests } } - private static void RunSta(Action action) - { - Exception? error = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { error = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(30)), "The STA test did not complete."); - if (error != null) - ExceptionDispatchInfo.Capture(error).Throw(); - } + private static void RunSta(Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(30), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/AutoNestFormStockOptionsTests.cs b/OpenNest.WinForms.Tests/Forms/AutoNestFormStockOptionsTests.cs index bdffd8c..4dd277d 100644 --- a/OpenNest.WinForms.Tests/Forms/AutoNestFormStockOptionsTests.cs +++ b/OpenNest.WinForms.Tests/Forms/AutoNestFormStockOptionsTests.cs @@ -2,7 +2,6 @@ using System.Collections; using System.ComponentModel; using System.Globalization; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Text.RegularExpressions; using System.Windows.Forms; using OpenNest.Forms; @@ -231,19 +230,6 @@ public class AutoNestFormStockOptionsTests private static void SetValue(object row, string property, object value) => TypeDescriptor.GetProperties(row)[property]!.SetValue(row, value); - private static void RunSta(Action action) - { - Exception? error = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { error = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(15)), "The STA test did not complete."); - if (error != null) - ExceptionDispatchInfo.Capture(error).Throw(); - } + private static void RunSta(Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(15), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/AutomaticCutOffFormTests.cs b/OpenNest.WinForms.Tests/Forms/AutomaticCutOffFormTests.cs index 5311d93..b350eff 100644 --- a/OpenNest.WinForms.Tests/Forms/AutomaticCutOffFormTests.cs +++ b/OpenNest.WinForms.Tests/Forms/AutomaticCutOffFormTests.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.CNC; using OpenNest.Controls; @@ -354,24 +353,6 @@ public class AutomaticCutOffFormTests typeof(AutomaticCutOffForm).GetMethod(method, BindingFlags.Instance | BindingFlags.NonPublic)! .Invoke(form, arguments); - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try - { - action(); - } - catch (Exception ex) - { - failure = ex; - } - }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, Timeout.InfiniteTimeSpan, "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/BestFitViewerOperationLifetimeTests.cs b/OpenNest.WinForms.Tests/Forms/BestFitViewerOperationLifetimeTests.cs index a911dae..24b571d 100644 --- a/OpenNest.WinForms.Tests/Forms/BestFitViewerOperationLifetimeTests.cs +++ b/OpenNest.WinForms.Tests/Forms/BestFitViewerOperationLifetimeTests.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.CNC; using OpenNest.Collections; @@ -408,19 +407,6 @@ public class BestFitViewerOperationLifetimeTests private static void Invoke(BestFitViewerForm form, string name, params object[] args) => typeof(BestFitViewerForm).GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic)!.Invoke(form, args); - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { failure = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(60)), "STA test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(60), "STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/EditNestFocusTraversalTests.cs b/OpenNest.WinForms.Tests/Forms/EditNestFocusTraversalTests.cs index b86e169..6c80e12 100644 --- a/OpenNest.WinForms.Tests/Forms/EditNestFocusTraversalTests.cs +++ b/OpenNest.WinForms.Tests/Forms/EditNestFocusTraversalTests.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.Forms; @@ -146,25 +145,6 @@ public class EditNestFocusTraversalTests tabControl.SelectedTab = Assert.IsType(plates.Parent); } - private static void RunSta(System.Action testBody) - { - Exception? testFailure = null; - var staThread = new Thread(() => - { - try - { - testBody(); - } - catch (Exception error) - { - testFailure = error; - } - }) - { IsBackground = true }; - staThread.SetApartmentState(ApartmentState.STA); - staThread.Start(); - Assert.True(staThread.Join(TimeSpan.FromSeconds(60)), "The focus traversal STA test did not complete."); - if (testFailure != null) - ExceptionDispatchInfo.Capture(testFailure).Throw(); - } + private static void RunSta(System.Action testBody) => + StaTestThread.Run(testBody, TimeSpan.FromSeconds(60), "The focus traversal STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/LeadInSidePanelTests.cs b/OpenNest.WinForms.Tests/Forms/LeadInSidePanelTests.cs index 80af5f0..a50445c 100644 --- a/OpenNest.WinForms.Tests/Forms/LeadInSidePanelTests.cs +++ b/OpenNest.WinForms.Tests/Forms/LeadInSidePanelTests.cs @@ -1,4 +1,3 @@ -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.Forms; @@ -40,25 +39,6 @@ public class LeadInSidePanelTests Assert.Equal("Select", form.PlateView.Status); }); - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try - { - action(); - } - catch (Exception ex) - { - failure = ex; - } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(60)), "The STA test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(60), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs b/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs index 700acc9..965bdb8 100644 --- a/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs +++ b/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.CNC; using OpenNest.Controls; @@ -299,25 +298,6 @@ public class NestReportExportTests internal override void ReportSuccess(string destination) => Successes.Add(destination); } - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try - { - action(); - } - catch (Exception ex) - { - failure = ex; - } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(90)), "The STA test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(90), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/NestValidationFormTests.cs b/OpenNest.WinForms.Tests/Forms/NestValidationFormTests.cs index 28556bc..1545d68 100644 --- a/OpenNest.WinForms.Tests/Forms/NestValidationFormTests.cs +++ b/OpenNest.WinForms.Tests/Forms/NestValidationFormTests.cs @@ -1,4 +1,3 @@ -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.Forms; @@ -62,14 +61,6 @@ public class NestValidationFormTests }); } - private static void RunSta(Action action) - { - Exception? error = null; - var thread = new Thread(() => { try { action(); } catch (Exception ex) { error = ex; } }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(15))); - if (error != null) - ExceptionDispatchInfo.Capture(error).Throw(); - } + private static void RunSta(Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(15), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/PlateExportTests.cs b/OpenNest.WinForms.Tests/Forms/PlateExportTests.cs index 184a88a..cfef914 100644 --- a/OpenNest.WinForms.Tests/Forms/PlateExportTests.cs +++ b/OpenNest.WinForms.Tests/Forms/PlateExportTests.cs @@ -1,7 +1,6 @@ using System.Drawing; using System.Globalization; using System.Reflection; -using System.Runtime.ExceptionServices; using OpenNest.CNC; using OpenNest.Data; using OpenNest.Forms; @@ -453,25 +452,6 @@ public class PlateExportTests public void Dispose() => Directory.Delete(DirectoryPath, true); } - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try - { - action(); - } - catch (Exception ex) - { - failure = ex; - } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(90)), "The STA test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(90), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/PlateViewFillLifetimeTests.cs b/OpenNest.WinForms.Tests/Forms/PlateViewFillLifetimeTests.cs index 2c2d0ed..d30edb2 100644 --- a/OpenNest.WinForms.Tests/Forms/PlateViewFillLifetimeTests.cs +++ b/OpenNest.WinForms.Tests/Forms/PlateViewFillLifetimeTests.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.CNC; using OpenNest.Controls; @@ -283,25 +282,6 @@ public class PlateViewFillLifetimeTests form.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.NonPublic)! .Invoke(form, arguments); - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try - { - action(); - } - catch (Exception ex) - { - failure = ex; - } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(60)), "The STA test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(60), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/Forms/PostVerificationFormTests.cs b/OpenNest.WinForms.Tests/Forms/PostVerificationFormTests.cs index 280fb7c..9824956 100644 --- a/OpenNest.WinForms.Tests/Forms/PostVerificationFormTests.cs +++ b/OpenNest.WinForms.Tests/Forms/PostVerificationFormTests.cs @@ -206,18 +206,6 @@ public class PostVerificationFormTests form.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.NonPublic)! .Invoke(form, arguments); - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { failure = ex; } - }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(30)), "STA verification test timed out."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(30), "STA verification test timed out."); } diff --git a/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs b/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs index df992f8..a06857c 100644 --- a/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs +++ b/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs @@ -1,7 +1,6 @@ using System.Collections.Concurrent; using System.Drawing; using System.Drawing.Drawing2D; -using System.Runtime.ExceptionServices; using System.Windows.Forms; using OpenNest.CNC; using OpenNest.Controls; @@ -1183,19 +1182,6 @@ public class PlateOverlapOverlayTests return new Part(new Drawing("same name", program), new Vector(x, 0)); } - private static void RunSta(System.Action action) - { - Exception? failure = null; - var thread = new Thread(() => - { - try { action(); } - catch (Exception ex) { failure = ex; } - }) - { IsBackground = true }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - Assert.True(thread.Join(TimeSpan.FromSeconds(60)), "The STA test did not complete."); - if (failure != null) - ExceptionDispatchInfo.Capture(failure).Throw(); - } + private static void RunSta(System.Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(60), "The STA test did not complete."); } diff --git a/OpenNest.WinForms.Tests/StaTestThread.cs b/OpenNest.WinForms.Tests/StaTestThread.cs new file mode 100644 index 0000000..d3e4626 --- /dev/null +++ b/OpenNest.WinForms.Tests/StaTestThread.cs @@ -0,0 +1,48 @@ +using System.Runtime.ExceptionServices; +using System.Windows.Forms; + +namespace OpenNest.WinForms.Tests; + +/// +/// Runs a test body on its own STA thread and rethrows its failure on the +/// calling test thread. +/// +/// +/// By default WinForms catches an exception raised while dispatching a window +/// message or a BeginInvoke/SynchronizationContext callback and hands it to the +/// thread's ThreadException handler. With none registered it opens a modal +/// ThreadExceptionDialog in an interactive session, which blocks the test until +/// its join timeout, and silently ignores the exception otherwise. Both the +/// unhandled-exception mode and the handler are per-thread, so they cannot be +/// configured once for the assembly: each STA thread selects +/// before it creates a +/// window, and the exception then propagates to the code pumping messages +/// (Show, DoEvents, ShowDialog) and fails the test. +/// +internal static class StaTestThread +{ + public static void Run(Action body, TimeSpan timeout, string timeoutMessage) + { + Exception? failure = null; + var thread = new Thread(() => + { + try + { + Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); + body(); + } + catch (Exception ex) + { + failure = ex; + } + }) + { + IsBackground = true, + }; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + Assert.True(thread.Join(timeout), timeoutMessage); + if (failure != null) + ExceptionDispatchInfo.Capture(failure).Throw(); + } +} diff --git a/OpenNest.WinForms.Tests/StaTestThreadTests.cs b/OpenNest.WinForms.Tests/StaTestThreadTests.cs new file mode 100644 index 0000000..00b62fb --- /dev/null +++ b/OpenNest.WinForms.Tests/StaTestThreadTests.cs @@ -0,0 +1,64 @@ +using System.Runtime.ExceptionServices; +using System.Windows.Forms; + +namespace OpenNest.WinForms.Tests; + +public class StaTestThreadTests +{ + private const string CallbackMessage = "thrown from a queued UI callback"; + + [Fact] + public void QueuedCallbackExceptionFailsTheTestInsteadOfOpeningADialog() + { + var error = Assert.Throws(() => + StaTestThread.Run( + ThrowFromQueuedCallback, + TimeSpan.FromSeconds(30), + "The STA test did not complete." + ) + ); + + Assert.Equal(CallbackMessage, error.Message); + } + + [Fact] + public void WithoutThrowExceptionModeTheCallbackExceptionNeverReachesTheTestBody() + { + // Control: the default mode routes the same exception to the thread's + // ThreadException handler (or, with none, to a modal dialog), so the + // test body itself completes without seeing it. + Exception? routed = null; + Exception? bodyFailure = null; + var thread = new Thread(() => + { + Application.ThreadException += (_, e) => routed = e.Exception; + try + { + ThrowFromQueuedCallback(); + } + catch (Exception ex) + { + bodyFailure = ex; + } + }) + { + IsBackground = true, + }; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + Assert.True(thread.Join(TimeSpan.FromSeconds(30)), "The control STA thread did not complete."); + if (bodyFailure != null) + ExceptionDispatchInfo.Capture(bodyFailure).Throw(); + Assert.IsType(routed); + Assert.Equal(CallbackMessage, routed!.Message); + } + + private static void ThrowFromQueuedCallback() + { + using var control = new Control(); + _ = control.Handle; + control.BeginInvoke(new Action(() => throw new InvalidOperationException(CallbackMessage))); + Application.DoEvents(); + } +} diff --git a/OpenNest.WinForms.Tests/TestAssemblySetup.cs b/OpenNest.WinForms.Tests/TestAssemblySetup.cs deleted file mode 100644 index 25f4609..0000000 --- a/OpenNest.WinForms.Tests/TestAssemblySetup.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Windows.Forms; - -namespace OpenNest.WinForms.Tests; - -internal static class TestAssemblySetup -{ - // SetUnhandledExceptionMode only changes which *default* dialog style WinForms would - // show; it still shows one. A registered ThreadException handler is what actually - // suppresses it (WinForms always prefers the handler over the default dialog), which - // matters here because none of our tests call Application.Run() to install one. - // Without this, an exception thrown inside a WndProc callback (e.g. a DataGridView - // commit failure during Show()) pops a modal ThreadExceptionDialog that blocks the - // desktop until someone dismisses it by hand, instead of failing the test normally. - [ModuleInitializer] - internal static void Initialize() - { - Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); - Application.ThreadException += (_, e) => - Console.Error.WriteLine($"Suppressed ThreadExceptionDialog: {e.Exception}"); - } -}