Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Do You Create A Global Error Handling for a MAUI App

Tags:

maui

Is there a way to set up a global error handler for a MAUI app? My app is currently running fine when I launch it from Visual Studio. However on all simulators if I try and start the app up from the shell's app icon, it starts up then quits (and on iOS I get a crash dump). I have no idea where this is happening at, so wanted to try and catch in a global handler.

I looked at builder.ConfigureMauiHandlers, but it appears to only work with controls. You can't use a type of UnhandledExceptionEventHandler, which is what I was thinking would be appropriate.

I also tried an AppDomain handler, but it doesn't not appear to work either:

AppDomain ad = AppDomain.CurrentDomain;
ad.UnhandledException += Ad_UnhandledException;

Is this possible? I've only found samples for WCF and they don't work.

like image 979
Steve Peschka Avatar asked Sep 06 '25 08:09

Steve Peschka


2 Answers

There is a pretty lengthy GitHub discussion here: Global exception handling #653

The most popular response is from one of the Sentry developers, who offered a gist solution using first chance exception.

The windows-specific implementation includes the following handlers, where _lastFirstChanceException is a private static variable.

    AppDomain.CurrentDomain.UnhandledException += (sender, args) => 
    {
        UnhandledException?.Invoke(sender, args);
    };

...

    AppDomain.CurrentDomain.FirstChanceException += (_, args) =>
    {
        _lastFirstChanceException = args.Exception;
    };

    Microsoft.UI.Xaml.Application.Current.UnhandledException += (sender, args) =>
    {
        var exception = args.Exception;

        if (exception.StackTrace is null)
        {
            exception = _lastFirstChanceException;
        }

        UnhandledException?.Invoke(sender, new UnhandledExceptionEventArgs(exception, true));
    };

While this worked for my use case, SonarLint did indicate that there were some issues with the static constructor approach. One solution to that would be to use a sealed MauiException class with a Lazy<MauiException> to access it as a threadsafe singleton, as Jon Skeet describes.

like image 64
dperish Avatar answered Sep 08 '25 22:09

dperish


For an app targeting iOS and Android this should be more than enough:

public static class GlobalExceptionHandler
{
    // We'll route all unhandled exceptions through this one event.
    public static event UnhandledExceptionEventHandler UnhandledException;

    static GlobalExceptionHandler()
    {
        AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
        {
            UnhandledException?.Invoke(sender, args);
        };

        // Events fired by the TaskScheduler. That is calls like Task.Run(...)     
        TaskScheduler.UnobservedTaskException += (sender, args) =>
        {
            UnhandledException?.Invoke(sender, new UnhandledExceptionEventArgs(args.Exception, false));
        };

#if IOS

        // For iOS and Mac Catalyst
        // Exceptions will flow through AppDomain.CurrentDomain.UnhandledException,
        // but we need to set UnwindNativeCode to get it to work correctly. 
        // 
        // See: https://github.com/xamarin/xamarin-macios/issues/15252
        
        ObjCRuntime.Runtime.MarshalManagedException += (_, args) =>
        {
            args.ExceptionMode = ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode;
        };

#elif ANDROID

        // For Android:
        // All exceptions will flow through Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser,
        // and NOT through AppDomain.CurrentDomain.UnhandledException

        Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += (sender, args) =>
        {
            args.Handled = true;
            UnhandledException?.Invoke(sender, new UnhandledExceptionEventArgs(args.Exception, true));
        };

        Java.Lang.Thread.DefaultUncaughtExceptionHandler = new CustomUncaughtExceptionHandler(e =>
            UnhandledException?.Invoke(null, new UnhandledExceptionEventArgs(e, true)));
#endif
    }
}

#if ANDROID
public class CustomUncaughtExceptionHandler(Action<Java.Lang.Throwable> callback)
    : Java.Lang.Object, Java.Lang.Thread.IUncaughtExceptionHandler
{
    public void UncaughtException(Java.Lang.Thread t, Java.Lang.Throwable e)
    {
        callback(e);
    }
}
#endif

And a simple way to use this would be:

GlobalExceptionHandler.UnhandledException += GlobalExceptionHandler_UnhandledException;

 private void GlobalExceptionHandler_UnhandledException(object sender, UnhandledExceptionEventArgs e)
  {
      var exception = e.ExceptionObject as System.Exception;
      // Log this exception or whatever
  }
like image 30
FreakyAli Avatar answered Sep 08 '25 23:09

FreakyAli