2009-09-24 6 views
272

Иногда при невоспроизводимых обстоятельствах приложение WPF вылетает без какого-либо сообщения. Приложение просто мгновенно закрывается.WPF-глобальный обработчик исключений

Где лучшее место для реализации глобального блока Try/Catch. По крайней мере, я должен реализовать MessageBox с: «Извините за неудобства ...»

+10

должен любить, как дублирующие ссылки на этот вопрос – Jay

+0

этот вопрос лучше ответить. –

ответ

138

Вы можете обращаться AppDomain.UnhandledException событие

EDIT: на самом деле, это событие, вероятно, более адекватным: Application.DispatcherUnhandledException

+8

Добавить обработчик в конструкторе форм следующим образом: AppDomain.Current.UnhandledException + = ... – Dabblernl

+11

Плохая идея, если вы создаете несколько экземпляров окна ... –

+1

Привет, Thomas, спасибо за ваш ответ. Appdomain.UnHandledException отлично работает для меня. –

428

Вы могут захватывать необработанные исключения на разных уровнях:

  1. AppDomain.CurrentDomain.UnhandledException Из всех тем в AppDomain.
  2. Dispatcher.UnhandledException Из одной конкретной диспетчерской нити пользовательского интерфейса.
  3. Application.Current.DispatcherUnhandledException От Главная Диспетчерский поток пользовательского интерфейса в приложении WPF.
  4. TaskScheduler.UnobservedTaskException из каждого AppDomain, который использует планировщик задач для асинхронных операций.

Вы должны подумать над тем, какой уровень вам нужен для захвата необработанных исключений.

Решение вопроса между # 2 и # 3 зависит от того, используете ли вы более одного потока WPF. Это довольно экзотическая ситуация, и если вы не уверены в том, есть вы или нет, то, скорее всего, вы этого не сделали.

+12

Примечание для вашего элемента № 3 мне пришлось поставить. Текущий следующий вид приложения: Application.Current.DispatcherUnhandledException + = ... –

+1

@Keith G - все эти события являются членами экземпляра, поэтому вам нужно будет подключить их для каждого требуемого объекта, в зависимости от ваших обстоятельств. –

+0

Если приложение просто закрывается, не обращаясь ни к одному из этих обработчиков исключений, также может быть, что система была отключена с помощью среды. FailFast – Mo0gles

89

Быстрый пример кода для Application.Dispatcher.UnhandledException:

public App() :base() { 
     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); 
     e.Handled = true; 
    } 

Я добавил этот код в App.xaml.cs

+0

+1 для кода вырезания/вставки. Если вы хотите активировать диалоговое окно с сообщением об ошибке, расширенный набор инструментов WPF имеет элемент управления [messagebox] (http://wpftoolkit.codeplex.com/wikipage?title=Extended%20WPF%20Toolkit%20Controls). –

+12

Обратите внимание, что в определенных ситуациях установка 'e.Handled = true' может привести к закрытию пользовательского интерфейса приложения, в то время как процесс остается бесшумным. – qJake

39

Я использую следующий код в моем WPF приложений, чтобы показать " Извините за неудобство "при каждом необработанном исключении. Он показывает сообщение об исключении и спрашивает пользователя, хотят ли они закрыть приложение или игнорировать исключение и продолжить (последний случай удобен, когда происходят нефатальные исключения, и пользователь по-прежнему может продолжать использовать приложение).

В App.xaml добавить обработчик события запуска:

<Application .... Startup="Application_Startup"> 

В App.xaml.cs коде добавить запуска функции обработчика событий, который будет регистрировать глобальный обработчик событий приложения:

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(); 
    } 
} 
+0

@Weston, что ссылка мертва – McKay

+2

@McKay был удален как обман этого: http://stackoverflow.com/questions/2004629/what-is-the-best-way-in-c-sharp-to-determine- is-the-programmer-is-running-t – weston

+0

@Weston Спасибо за обновление. – McKay

10

В с добавлением к статьям выше:

Application.Current.DispatcherUnhandledException 

не будет ловить исключения, которые выбрасываются из er, затем основной поток. Вы должны обрабатывать эти исключения в своей реальной теме.Но если вы хотите обрабатывать их на глобальный обработчик исключений вы можете передать его в основной поток:

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); 
     } 
    }); 
1

Как уже упоминалось выше

Application.Current.DispatcherUnhandledException не поймаешь исключения, которые выбрасываются из другого потока, затем основного потока.

Это фактическая зависит от того, как поток был создан

Один случай, который не обрабатывается Application.Current.DispatcherUnhandledException является System.Windows.Forms.Timer, для которых Application.ThreadException может быть использован для обработки этих при запуске формы на других потоках, кроме основного потока вам необходимо будет установить Application.ThreadException от каждой такой нити

+0

копирование с другого потока в SO ответ по Герцель Guinness: <конфигурация> в app.config будет препятствовать вашему вторичных потоков исключение от закрытия вниз приложение " –

3

полное решение here

это объясняется очень хорошо с образцом кода. Однако будьте осторожны, чтобы он не закрыл приложение. Добавьте строку Application.Current.Shutdown(); , чтобы изящно закрыть приложение.

7

Лучшим ответом является, вероятно, https://stackoverflow.com/a/1472562/601990.

Вот код, который показывает, как использовать его:

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); 
    } 
} 
+0

Возможно, необходимо включить' #if DEBUG', поэтому Visual Studio обрабатывает исключения, такие как обычные только при отладке. Удивительное решение. –

+1

вместо '#if DEBUG' вы должны использовать' [Условный («DEBUG») ] 'на' RegisterGlobalExceptionHandling'. Таким образом, вы можете гарантировать, что код компилируется при изменении цели компилятора. – MovGP0

+0

, кроме того, предпочтительно вести журнал glo бл исключения также в производственном кодексе. вы можете использовать 'ConditionalAttribute' для конфигурации регистратора внутри вашей установки инъекции зависимостей и просто изменить многостраничность протоколирования. – MovGP0

Смежные вопросы