(转载)UWP 异步方法中的异常无法被 App 的 UnhandledException 捕获的问题

该篇文章转载自:博客园

目前常见的做法是局部 try catch,但是这样做很容易导致 process 被强制终止从而引起闪退。我这里用了一个线程同步模型类解决这个问题。

using System;
using System.Threading;
using Windows.UI.Xaml.Controls;

namespace AiJianShu.ExceptionHandler
{
	internal class ExceptionHandlingSynchronizationContext : SynchronizationContext
	{
		/// <summary>
		/// 注册事件.  需要在OnLaunched和OnActivated事件中调用
		/// </summary>
		/// <returns></returns>
		public static ExceptionHandlingSynchronizationContext Register()
		{
			var syncContext = Current;
			if (syncContext == null)
				throw new InvalidOperationException("Ensure a synchronization context exists before calling this method.");


			var customSynchronizationContext = syncContext as ExceptionHandlingSynchronizationContext;


			if (customSynchronizationContext == null)
			{
				customSynchronizationContext = new ExceptionHandlingSynchronizationContext(syncContext);
				SetSynchronizationContext(customSynchronizationContext);
			}


			return customSynchronizationContext;
		}

		/// <summary>
		/// 将线程的上下文绑定到特定的Frame上面
		/// </summary>
		/// <param name="rootFrame"></param>
		/// <returns></returns>
		public static ExceptionHandlingSynchronizationContext RegisterForFrame(Frame rootFrame)
		{
			if (rootFrame == null)
				throw new ArgumentNullException("rootFrame");

			var synchronizationContext = Register();

			rootFrame.Navigating += (sender, args) => EnsureContext(synchronizationContext);
			rootFrame.Loaded += (sender, args) => EnsureContext(synchronizationContext);

			return synchronizationContext;
		}

		private static void EnsureContext(SynchronizationContext context)
		{
			if (Current != context)
				SetSynchronizationContext(context);
		}


		private readonly SynchronizationContext _syncContext;


		public ExceptionHandlingSynchronizationContext(SynchronizationContext syncContext)
		{
			_syncContext = syncContext;
		}


		public override SynchronizationContext CreateCopy()
		{
			return new ExceptionHandlingSynchronizationContext(_syncContext.CreateCopy());
		}


		public override void OperationCompleted()
		{
			_syncContext.OperationCompleted();
		}


		public override void OperationStarted()
		{
			_syncContext.OperationStarted();
		}


		public override void Post(SendOrPostCallback d, object state)
		{
			_syncContext.Post(WrapCallback(d), state);
		}


		public override void Send(SendOrPostCallback d, object state)
		{
			_syncContext.Send(d, state);
		}


		private SendOrPostCallback WrapCallback(SendOrPostCallback sendOrPostCallback)
		{
			return state =>
			{
				try
				{
					sendOrPostCallback(state);
				}
				catch (Exception ex)
				{
					if (!HandleException(ex))
						throw;
				}
			};
		}

		private bool HandleException(Exception exception)
		{
			if (UnhandledException == null)
				return false;

			var exWrapper = new AysncUnhandledExceptionEventArgs
			{
				Exception = exception
			};

			UnhandledException(this, exWrapper);

#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
			if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
#endif
			return exWrapper.Handled;
		}

		public event EventHandler<AysncUnhandledExceptionEventArgs> UnhandledException;
	}

	public class AysncUnhandledExceptionEventArgs : EventArgs
	{
		public bool Handled { get; set; }
		public Exception Exception { get; set; }
	}
}

使用实例:

public App()
{
	this.InitializeComponent();
	this.Suspending += OnSuspending;

	this.UnhandledException += App_UnhandledException;

}

private void RegisterExceptionHandlingSynchronizationContext()
{
	ExceptionHandlingSynchronizationContext
		.Register()
		.UnhandledException += SynchronizationContext_UnhandledException;
}

private async void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
{
	e.Handled = true;

	await new MessageDialog("Application Unhandled Exception:\r\n" + e.Exception.Message)
		.ShowAsync();
}

private async void SynchronizationContext_UnhandledException(object sender, AysncUnhandledExceptionEventArgs e)
{
	e.Handled = true;

	await new MessageDialog("Synchronization Context Unhandled Exception:\r\n" + e.Exception.Message)
		.ShowAsync();
}

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
	RegisterExceptionHandlingSynchronizationContext();

#if DEBUG
	if (System.Diagnostics.Debugger.IsAttached)
	{
		this.DebugSettings.EnableFrameRateCounter = true;
	}
#endif

	Frame rootFrame = Window.Current.Content as Frame;

	// 不要在窗口已包含内容时重复应用程序初始化,
	// 只需确保窗口处于活动状态
	if (rootFrame == null)
	{
		// 创建要充当导航上下文的框架,并导航到第一页
		rootFrame = new Frame();

		rootFrame.NavigationFailed += OnNavigationFailed;

		if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
		{
			//TODO: 从之前挂起的应用程序加载状态
		}

		// 将框架放在当前窗口中
		Window.Current.Content = rootFrame;
	}

	if (rootFrame.Content == null)
	{
		// 当导航堆栈尚未还原时,导航到第一页,
		// 并通过将所需信息作为导航参数传入来配置
		// 参数
		rootFrame.Navigate(typeof(MainPage), e.Arguments);
	}
	// 确保当前窗口处于活动状态
	Window.Current.Activate();
}

protected override void OnActivated(IActivatedEventArgs args)
{
	RegisterExceptionHandlingSynchronizationContext();
	base.OnActivated(args);
}

这样全局的异常就都能在 App.xaml.cs 文件中被捕获,不会导致闪退。

posted @ 2021-06-26 22:07  摘叶飞镖  阅读(118)  评论(0编辑  收藏  举报