-
-
Save mattjohnsonpint/7b385b7a2da7059c4a16562bc5ddb3b7 to your computer and use it in GitHub Desktop.
| MIT License | |
| Copyright (c) 2022 Matt Johnson-Pint | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| SOFTWARE. |
| public static class MauiExceptions | |
| { | |
| #if WINDOWS | |
| private static Exception _lastFirstChanceException; | |
| #endif | |
| // We'll route all unhandled exceptions through this one event. | |
| public static event UnhandledExceptionEventHandler UnhandledException; | |
| static MauiExceptions() | |
| { | |
| // This is the normal event expected, and should still be used. | |
| // It will fire for exceptions from iOS and Mac Catalyst, | |
| // and for exceptions on background threads from WinUI 3. | |
| AppDomain.CurrentDomain.UnhandledException += (sender, args) => | |
| { | |
| UnhandledException?.Invoke(sender, args); | |
| }; | |
| #if IOS || MACCATALYST | |
| // 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) => | |
| { | |
| UnhandledException?.Invoke(sender, new UnhandledExceptionEventArgs(args.Exception, true)); | |
| }; | |
| #elif WINDOWS | |
| // For WinUI 3: | |
| // | |
| // * Exceptions on background threads are caught by AppDomain.CurrentDomain.UnhandledException, | |
| // not by Microsoft.UI.Xaml.Application.Current.UnhandledException | |
| // See: https://github.com/microsoft/microsoft-ui-xaml/issues/5221 | |
| // | |
| // * Exceptions caught by Microsoft.UI.Xaml.Application.Current.UnhandledException have details removed, | |
| // but that can be worked around by saved by trapping first chance exceptions | |
| // See: https://github.com/microsoft/microsoft-ui-xaml/issues/7160 | |
| // | |
| 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)); | |
| }; | |
| #endif | |
| } | |
| } |
@jonpryor Interesting, in my case none of the other exception handlers were invoked in scenarios like ClassNotFoundException (related to broken bindings) so I had to register Thread.DefaultUncaughtExceptionHandler where I got the callback and details I needed.
I have followed what everybody has mentioned here. Though the customer exception handler code is getting executed which i can verify from the console during debug run on android emulator as well as on real device, app still closes down. I can't get to handle the error and keep the app open. Is that the expected behaviour? Is there a way i can keep the app open and show a error alert?
this is my code
` public static class GlobalExceptions
{
#if WINDOWS
private static Exception _lastFirstChanceException;
#endif
// We'll route all unhandled exceptions through this one event.
public static event UnhandledExceptionEventHandler UnhandledException;
static GlobalExceptions()
{
// This is the normal event expected, and should still be used.
// It will fire for exceptions from iOS and Mac Catalyst,
// and for exceptions on background threads from WinUI 3.
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: dotnet/macios#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, false)));
#elif WINDOWS
// For WinUI 3:
//
// * Exceptions on background threads are caught by AppDomain.CurrentDomain.UnhandledException,
// not by Microsoft.UI.Xaml.Application.Current.UnhandledException
// See: https://github.com/microsoft/microsoft-ui-xaml/issues/5221
//
// * Exceptions caught by Microsoft.UI.Xaml.Application.Current.UnhandledException have details removed,
// but that can be worked around by saved by trapping first chance exceptions
// See: https://github.com/microsoft/microsoft-ui-xaml/issues/7160
//
AppDomain.CurrentDomain.FirstChanceException += (_, args) =>
{
args.Handled = true;
_lastFirstChanceException = args.Exception;
};
Microsoft.UI.Xaml.Application.Current.UnhandledException += (sender, args) =>
{
var exception = args.Exception;
if (exception.StackTrace is null)
{
exception = _lastFirstChanceException;
}
args.Handled = true;
UnhandledException?.Invoke(sender, new UnhandledExceptionEventArgs(exception, 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
}`
@Danielku15, @rumbledot: you shouldn't need to set
Thread.DefaultUncaughtExceptionHandler, as .NET for Android already sets that property and forwards exceptions to theAppDomain.UnhandledExceptionevent: