using Avalonia.Threading; using LehrerApp.Core.Services; namespace LehrerApp.Desktop.Services; /// /// Zentrale Exception-Behandlung (13.2.1): protokolliert jeden unerwarteten Fehler und zeigt /// eine verständliche Meldung statt eines rohen Absturzes. /// /// .UIThread.UnhandledException fängt Fehler aus Befehlen und /// Ereignis-Handlern ab. Nur erwartbar wiederherstellbare I/O-, Netzwerk-, Timeout- und /// Abbruchfehler werden behandelt; unbekannte Zustandsfehler dürfen die App beenden. /// AppDomain.UnhandledException und TaskScheduler.UnobservedTaskException sind /// Sicherheitsnetze für Fehler außerhalb des UI-Threads; die App kann eine "IsTerminating"- /// Ausnahme dort nicht mehr verhindern, aber wenigstens vollständig protokollieren, bevor sie endet. /// public static class GlobalExceptionHandler { private static AppLogger? _logger; private static NotificationService? _notifications; public static void Install(AppLogger logger, NotificationService? notifications = null) { _logger = logger; _notifications = notifications; Dispatcher.UIThread.UnhandledException += OnDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; } /// Muss aufgerufen werden, sobald der DI-Container steht, damit spätere Fehler /// (die vor dem Container auftreten könnten) noch als Toast sichtbar werden. public static void AttachNotifications(NotificationService notifications) => _notifications = notifications; private static void OnDispatcherUnhandledException(object? sender, DispatcherUnhandledExceptionEventArgs e) { _logger?.Error("Unbehandelter Fehler auf dem UI-Thread.", e.Exception); e.Handled = IsRecoverable(e.Exception); if (e.Handled) _notifications?.ShowError("Der Vorgang ist fehlgeschlagen. Details wurden protokolliert."); } private static bool IsRecoverable(Exception exception) => exception is IOException or UnauthorizedAccessException or HttpRequestException or TimeoutException or OperationCanceledException; private static void OnAppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) { _logger?.Error( $"Unbehandelter Fehler außerhalb des UI-Threads (IsTerminating={e.IsTerminating}).", e.ExceptionObject as Exception); } private static void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { _logger?.Error("Unbeobachtete Ausnahme in einem Hintergrund-Task.", e.Exception); e.SetObserved(); // verhindert, dass die Ausnahme unbemerkt verschwindet. } }