From 212372db08634bf49a3bc7fcda30b2b4a79ceddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20K=C3=B6nig?= Date: Sun, 24 Mar 2024 11:26:34 +0100 Subject: [PATCH] Added global exception handling to TestApp --- RolandK.AvaloniaExtensions.sln | 6 ++++ .../GlobalErrorReporting.cs | 5 ++- .../App.axaml | 8 +++++ .../App.axaml.cs | 12 +++++++ .../Program.cs | 21 +++++++++++ ...aExtensions.TestApp.ExceptionViewer.csproj | 22 ++++++++++++ .../MainWindow.axaml | 8 +++++ .../MainWindow.axaml.cs | 35 +++++++++++++++++++ .../Program.cs | 18 ++++++++-- .../RolandK.AvaloniaExtensions.TestApp.csproj | 2 ++ 10 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml create mode 100644 src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml.cs create mode 100644 src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/Program.cs create mode 100644 src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer.csproj diff --git a/RolandK.AvaloniaExtensions.sln b/RolandK.AvaloniaExtensions.sln index a7c630c..922365c 100644 --- a/RolandK.AvaloniaExtensions.sln +++ b/RolandK.AvaloniaExtensions.sln @@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RolandK.AvaloniaExtensions. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RolandK.AvaloniaExtensions.ExceptionHandling", "src\RolandK.AvaloniaExtensions.ExceptionHandling\RolandK.AvaloniaExtensions.ExceptionHandling.csproj", "{FC6CF99C-571A-454F-B385-52F0DEC21BFA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RolandK.AvaloniaExtensions.TestApp.ExceptionViewer", "src\RolandK.AvaloniaExtensions.TestApp.ExceptionViewer\RolandK.AvaloniaExtensions.TestApp.ExceptionViewer.csproj", "{ED908FBE-7113-4E38-A725-89AA6B4BA9C2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {FC6CF99C-571A-454F-B385-52F0DEC21BFA}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC6CF99C-571A-454F-B385-52F0DEC21BFA}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC6CF99C-571A-454F-B385-52F0DEC21BFA}.Release|Any CPU.Build.0 = Release|Any CPU + {ED908FBE-7113-4E38-A725-89AA6B4BA9C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED908FBE-7113-4E38-A725-89AA6B4BA9C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED908FBE-7113-4E38-A725-89AA6B4BA9C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED908FBE-7113-4E38-A725-89AA6B4BA9C2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/RolandK.AvaloniaExtensions.ExceptionHandling/GlobalErrorReporting.cs b/src/RolandK.AvaloniaExtensions.ExceptionHandling/GlobalErrorReporting.cs index a5e0778..d6b4492 100644 --- a/src/RolandK.AvaloniaExtensions.ExceptionHandling/GlobalErrorReporting.cs +++ b/src/RolandK.AvaloniaExtensions.ExceptionHandling/GlobalErrorReporting.cs @@ -33,12 +33,15 @@ public static async Task ShowGlobalExceptionDialogAsync( /// /// Tries to show an error dialog with some exception details. /// If it is not possible for any reason, this method simply does nothing. + /// + /// This call is a blocking call. It es meant to be called within a global + /// try-catch block in Program.cs /// /// The exception to be shown to the user. /// This should be a technical name, the method uses it to create a temporary directory in the filesystem. /// The project name of the executable showing the error dialog. /// If null, a default collection of IExceptionAnalyzers ist used. - public static void TryShowGlobalExceptionDialogInAnotherProcess( + public static void TryShowBlockingGlobalExceptionDialogInAnotherProcess( Exception exception, string applicationTempDirectoryName, string exceptionViewerExecutableProjectName, diff --git a/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml new file mode 100644 index 0000000..796ddca --- /dev/null +++ b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml.cs b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml.cs new file mode 100644 index 0000000..9c39079 --- /dev/null +++ b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/App.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Markup.Xaml; +using RolandK.AvaloniaExtensions.ExceptionHandling; + +namespace RolandK.AvaloniaExtensions.TestApp.ExceptionViewer; + +public partial class App : ExceptionViewerApplication +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } +} \ No newline at end of file diff --git a/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/Program.cs b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/Program.cs new file mode 100644 index 0000000..966ab55 --- /dev/null +++ b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/Program.cs @@ -0,0 +1,21 @@ +using Avalonia; +using System; + +namespace RolandK.AvaloniaExtensions.TestApp.ExceptionViewer; + +internal class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} \ No newline at end of file diff --git a/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer.csproj b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer.csproj new file mode 100644 index 0000000..c8df37f --- /dev/null +++ b/src/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer/RolandK.AvaloniaExtensions.TestApp.ExceptionViewer.csproj @@ -0,0 +1,22 @@ + + + WinExe + net8.0 + enable + false + copyused + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml b/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml index cc30298..3e5d769 100644 --- a/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml +++ b/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml @@ -35,6 +35,14 @@ Command="{Binding Path=RecreateTestDataCommand}"/> + + + + + diff --git a/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml.cs b/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml.cs index bbb33c3..a1dc699 100644 --- a/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml.cs +++ b/src/RolandK.AvaloniaExtensions.TestApp/MainWindow.axaml.cs @@ -1,3 +1,6 @@ +using System; +using Avalonia.Interactivity; +using RolandK.AvaloniaExtensions.ExceptionHandling; using RolandK.AvaloniaExtensions.Mvvm.Controls; namespace RolandK.AvaloniaExtensions.TestApp; @@ -8,4 +11,36 @@ public MainWindow() { this.InitializeComponent(); } + + private async void OnMnu_ShowDummyException_ThisProcess_Click(object? sender, RoutedEventArgs e) + { + try + { + throw new InvalidOperationException("Dummy exception"); + } + catch (Exception ex) + { + await GlobalErrorReporting.ShowGlobalExceptionDialogAsync(ex, this); + } + } + + private void OnMnu_ShowDummyException_OtherProcess_Click(object? sender, RoutedEventArgs e) + { + try + { + throw new InvalidOperationException("Dummy exception"); + } + catch (Exception ex) + { + GlobalErrorReporting.TryShowBlockingGlobalExceptionDialogInAnotherProcess( + ex, + ".RKAvaloniaExtensions.TestApp", + "RolandK.AvaloniaExtensions.TestApp.ExceptionViewer"); + } + } + + private void OnMnu_SimulateUnhandledException_Click(object? sender, RoutedEventArgs e) + { + throw new InvalidOperationException("Dummy exception"); + } } diff --git a/src/RolandK.AvaloniaExtensions.TestApp/Program.cs b/src/RolandK.AvaloniaExtensions.TestApp/Program.cs index ba9a430..4d7f355 100644 --- a/src/RolandK.AvaloniaExtensions.TestApp/Program.cs +++ b/src/RolandK.AvaloniaExtensions.TestApp/Program.cs @@ -2,6 +2,7 @@ using Avalonia; using Microsoft.Extensions.DependencyInjection; using RolandK.AvaloniaExtensions.DependencyInjection; +using RolandK.AvaloniaExtensions.ExceptionHandling; using RolandK.AvaloniaExtensions.TestApp.Services; namespace RolandK.AvaloniaExtensions.TestApp; @@ -12,8 +13,21 @@ public static class Program // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + public static void Main(string[] args) + { + try + { + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } + catch (Exception ex) + { + GlobalErrorReporting.TryShowBlockingGlobalExceptionDialogInAnotherProcess( + ex, + ".RKAvaloniaExtensions.TestApp", + "RolandK.AvaloniaExtensions.TestApp.ExceptionViewer"); + } + } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() diff --git a/src/RolandK.AvaloniaExtensions.TestApp/RolandK.AvaloniaExtensions.TestApp.csproj b/src/RolandK.AvaloniaExtensions.TestApp/RolandK.AvaloniaExtensions.TestApp.csproj index 2461c58..ab10d27 100644 --- a/src/RolandK.AvaloniaExtensions.TestApp/RolandK.AvaloniaExtensions.TestApp.csproj +++ b/src/RolandK.AvaloniaExtensions.TestApp/RolandK.AvaloniaExtensions.TestApp.csproj @@ -30,6 +30,8 @@ + +