時々、再現性のない状況で、WPFアプリケーションがメッセージなしでクラッシュします。アプリケーションはすぐに終了します。
グローバルなTry/Catchブロックを実装するのに最適な場所はどこですか。少なくとも、「ご不便をおかけして申し訳ありません...」というメッセージボックスを実装する必要があります。
AppDomain.UnhandledException
イベントを処理できます
編集:実際には、このイベントはおそらくもっと適切です。 Application.DispatcherUnhandledException
さまざまなレベルで未処理の例外をトラップできます。
AppDomain.CurrentDomain.UnhandledException
AppDomain内のすべてのスレッドから。Dispatcher.UnhandledException
単一の特定のUIディスパッチャスレッドから。Application.Current.DispatcherUnhandledException
WPFアプリケーションのmainUIディスパッチャースレッドから。TaskScheduler.UnobservedTaskException
非同期操作にタスクスケジューラを使用する各AppDomain内から。未処理の例外をトラップする必要があるレベルを検討する必要があります。
#2と#3のどちらを決定するかは、複数のWPFスレッドを使用しているかどうかによって異なります。これは非常にエキゾチックな状況であり、あなたがそうであるかどうかわからない場合は、そうではない可能性が高いです。
Application.Dispatcher.UnhandledExceptionのコードの簡単な例:
public App() {
this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}
void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {
string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
// OR whatever you want like logging etc. MessageBox it's just example
// for quick debugging etc.
e.Handled = true;
}
このコードをApp.xaml.csに追加しました
WPFアプリで次のコードを使用して、未処理の例外が発生するたびに「ご不便をおかけして申し訳ありません」ダイアログボックスを表示します。例外メッセージを表示し、アプリを閉じるか、例外を無視して続行するかをユーザーに尋ねます(後者の場合は、致命的でない例外が発生し、ユーザーが引き続きアプリを引き続き使用できる場合に便利です)。
App.xamlで、スタートアップイベントハンドラーを追加します。
<Application .... Startup="Application_Startup">
App.xaml.csコードで、グローバルアプリケーションイベントハンドラーを登録するStartupイベントハンドラー関数を追加します。
using System.Windows.Threading;
private void Application_Startup(object sender, StartupEventArgs e)
{
// Global exception handling
Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(AppDispatcherUnhandledException);
}
void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
\#if DEBUG // In debug mode do not custom-handle the exception, let Visual Studio handle it
e.Handled = false;
\#else
ShowUnhandledException(e);
\#endif
}
void ShowUnhandledException(DispatcherUnhandledExceptionEventArgs e)
{
e.Handled = true;
string errorMessage = string.Format("An application error occurred.\nPlease check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction in the application, and you better close it.\n\nError: {0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)",
e.Exception.Message + (e.Exception.InnerException != null ? "\n" +
e.Exception.InnerException.Message : null));
if (MessageBox.Show(errorMessage, "Application Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No) {
if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
Application.Current.Shutdown();
}
}
最良の答えはおそらく https://stackoverflow.com/a/1472562/60199 です。
使用方法を示すコードを次に示します。
App.xaml.cs
public sealed partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
// setting up the Dependency Injection container
var resolver = ResolverFactory.Get();
// getting the ILogger or ILog interface
var logger = resolver.Resolve<ILogger>();
RegisterGlobalExceptionHandling(logger);
// Bootstrapping Dependency Injection
// injects ViewModel into MainWindow.xaml
// remember to remove the StartupUri attribute in App.xaml
var mainWindow = resolver.Resolve<Pages.MainWindow>();
mainWindow.Show();
}
private void RegisterGlobalExceptionHandling(ILogger log)
{
// this is the line you really want
AppDomain.CurrentDomain.UnhandledException +=
(sender, args) => CurrentDomainOnUnhandledException(args, log);
// optional: hooking up some more handlers
// remember that you need to hook up additional handlers when
// logging from other dispatchers, shedulers, or applications
Application.Dispatcher.UnhandledException +=
(sender, args) => DispatcherOnUnhandledException(args, log);
Application.Current.DispatcherUnhandledException +=
(sender, args) => CurrentOnDispatcherUnhandledException(args, log);
TaskScheduler.UnobservedTaskException +=
(sender, args) => TaskSchedulerOnUnobservedTaskException(args, log);
}
private static void TaskSchedulerOnUnobservedTaskException(UnobservedTaskExceptionEventArgs args, ILogger log)
{
log.Error(args.Exception, args.Exception.Message);
args.SetObserved();
}
private static void CurrentOnDispatcherUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
{
log.Error(args.Exception, args.Exception.Message);
// args.Handled = true;
}
private static void DispatcherOnUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
{
log.Error(args.Exception, args.Exception.Message);
// args.Handled = true;
}
private static void CurrentDomainOnUnhandledException(UnhandledExceptionEventArgs args, ILogger log)
{
var exception = args.ExceptionObject as Exception;
var terminatingMessage = args.IsTerminating ? " The application is terminating." : string.Empty;
var exceptionMessage = exception?.Message ?? "An unmanaged exception occured.";
var message = string.Concat(exceptionMessage, terminatingMessage);
log.Error(exception, message);
}
}
上記の投稿に加えて:
Application.Current.DispatcherUnhandledException
メインスレッドから別のスレッドからスローされた例外をキャッチしません。実際のスレッドでこれらの例外を処理する必要があります。ただし、グローバル例外ハンドラでそれらを処理する場合は、メインスレッドに渡すことができます。
System.Threading.Thread t = new System.Threading.Thread(() =>
{
try
{
...
//this exception will not be catched by
//Application.DispatcherUnhandledException
throw new Exception("huh..");
...
}
catch (Exception ex)
{
//But we can handle it in the throwing thread
//and pass it to the main thread wehre Application.
//DispatcherUnhandledException can handle it
System.Windows.Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action<Exception>((exc) =>
{
throw new Exception("Exception from another Thread", exc);
}), ex);
}
});
Thomasの答えを補足するために、Application
クラスには、処理可能な DispatcherUnhandledException
イベントもあります。
完全なソリューションは here です
サンプルコードを使用して非常にわかりやすく説明されています。ただし、アプリケーションを閉じないように注意してください。Application.Current.Shutdown();行を追加します。アプリを正常に閉じます。
上記のように
Application.Current.DispatcherUnhandledExceptionは、メインスレッド以外の別のスレッドからスローされた例外をキャッチしません。
実際には、スレッドの作成方法に依存します
Application.Current.DispatcherUnhandledExceptionによって処理されないケースの1つは、Application.ThreadExceptionを設定する必要があるメインスレッド以外のスレッドでFormsを実行する場合にApplication.ThreadExceptionを使用してこれらを処理できるSystem.Windows.Forms.Timerです。そのような各スレッドから