web-dev-qa-db-ja.com

WPFアプリケーションで例外をグローバルにキャッチしますか?

実行時にその一部が例外をスローするWPFアプリケーションがあります。未処理の例外をグローバルにキャッチしてログに記録しますが、それ以外の場合はプログラムの実行を継続します(VBのOn Error Resume Nextなど)。

これはC#で可能ですか?もしそうなら、例外処理コードを正確にどこに置く必要がありますか?

現在、try/catchをラップして、発生する可能性のあるすべての例外をキャッチできる単一のポイントはありません。そして、それでもキャッチのために実行されたものは何でも残していたでしょう。または、ここで恐ろしく間違った方向に考えていますか?

ETA:以下の多くの人が指摘しているため、このアプリケーションは原子力発電所を制御するためのものではありません。それがクラッシュする場合、それはそれほど大したことではありませんが、主にUI関連のランダムな例外は、それが使用されるコンテキストで迷惑です。プラグインアーキテクチャを使用し、他のユーザー(その場合は学生も使用可能です。したがって、no完全にエラーのないコードを書くことができる経験豊富な開発者)。

キャッチされる例外については、完全なスタックトレースを含め、ログファイルに記録します。それがその演習の全体のポイントでした。 VBのOERNに文字通りあまりにも私の類似性を取っている人々に対抗するためだけに。

特定のクラスのエラーを盲目的に無視することは危険であり、アプリケーションインスタンスが破損する可能性があることを知っています。前述のように、このプログラムは誰にとってもミッションクリティカルではありません。彼らの正しい心の誰も、それに人間の文明の生存を賭けないでしょう。これは、特定の設計アプローチをテストするための単なる小さなツールです。ソフトウェア工学。

アプリケーションをすぐに使用する場合、例外で発生する可能性のあるものは多くありません。

  • 例外処理なし-エラーダイアログとアプリケーション終了。おそらく別の主題で実験を繰り返す必要があります。エラーは記録されていませんが、残念です。
  • 汎用的な例外処理–害のない良性のエラーがトラップされます。これは、開発中に発生したすべてのエラーから判断される一般的なケースです。この種のエラーを無視しても、すぐに結果が生じることはありません。コアデータ構造は十分にテストされているので、簡単にこれに耐えることができます。
  • 一般的な例外処理-トラップされた重大なエラー。後でクラッシュする可能性があります。これはめったに起こりません。今まで見たことがない。とにかくエラーは記録され、クラッシュは避けられないかもしれません。したがって、これは最初のケースと概念的に似ています。スタックトレースがあることを除きます。そして、ほとんどの場合、ユーザーは気づきさえしません。

プログラムによって生成された実験データに関して:深刻なエラーは最悪の場合、データが記録されないだけです。実験の結果をわずかに変更する微妙な変更はほとんどありません。その場合でも、結果が疑わしい場合は、エラーがログに記録されました。全体の外れ値であれば、そのデータポイントを捨てることができます。

要約すると、はい、私は自分自身を少なくとも部分的に正気であると考えています。また、プログラムを必ずしも完全に悪とするグローバルな例外処理ルーチンを考えていません。前述のように、そのような決定は、アプリケーションに応じて有効な場合があります。この場合、それは完全で全くのでたらめではなく、有効な決定であると判断されました。 他のアプリケーションでは、決定が異なるように見えるかもしれません。しかし、エラーを無視しているという理由だけで、私やそのプロジェクトに携わった他の人が世界を爆破する可能性があると非難しないでください。

サイドノート:そのアプリケーションには1人のユーザーがいます。 WindowsやOfficeのように、例外をユーザーにバブルさせるコストがそもそも非常に異なっている何百万人もの人々によって使用されるものではありません。

221
Joey

Application.DispatcherUnhandledException Event を使用します。概要については この質問 を参照してください( Drew Noakesの回答 を参照)。

データベースへの保存中にスタックオーバーフロー、メモリ不足、ネットワーク接続の喪失などが発生した場合、アプリケーションを正常に再開できないという例外がまだあることに注意してください。

173
David Schmitt

NLogを使用して、AppDomain内のすべてのスレッドIディスパッチャスレッド、およびasyncからスローされた例外をキャッチするサンプルコード関数

App.xaml.cs:

public partial class App : Application
{
    private static Logger _logger = LogManager.GetCurrentClassLogger();

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        SetupExceptionHandling();
    }

    private void SetupExceptionHandling()
    {
        AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");

        DispatcherUnhandledException += (s, e) =>
            LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");

        TaskScheduler.UnobservedTaskException += (s, e) =>
            LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
    }

    private void LogUnhandledException(Exception exception, string source)
    {
        string message = $"Unhandled exception ({source})";
        try
        {
            System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
            message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Exception in LogUnhandledException");
        }
        finally
        {
            _logger.Error(exception, message);
        }
    }
34

AppDomain.UnhandledException イベント

このイベントは、キャッチされなかった例外の通知を提供します。これにより、システムのデフォルトハンドラーがユーザーに例外を報告し、アプリケーションを終了する前に、アプリケーションが例外に関する情報を記録できます。

   public App()
   {
      AppDomain currentDomain = AppDomain.CurrentDomain;
      currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);    
   }

   static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
   {
      Exception e = (Exception) args.ExceptionObject;
      Console.WriteLine("MyHandler caught : " + e.Message);
      Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
   }

UnhandledExceptionイベントがデフォルトのアプリケーションドメインで処理される場合、どのアプリケーションドメインでスレッドが開始されたかに関係なく、スレッドの未処理の例外に対してそこで発生します。そのアプリケーションドメインでイベントが発生します。そのアプリケーションドメインが既定のアプリケーションドメインではなく、既定のアプリケーションドメインにもイベントハンドラーがある場合、イベントは両方のアプリケーションドメインで発生します。

たとえば、スレッドがアプリケーションドメイン "AD1"で開始し、アプリケーションドメイン "AD2"のメソッドを呼び出し、そこから例外をスローするアプリケーションドメイン "AD3"のメソッドを呼び出すとします。 UnhandledExceptionイベントを発生させることができる最初のアプリケーションドメインは「AD1」です。そのアプリケーションドメインが既定のアプリケーションドメインでない場合、イベントは既定のアプリケーションドメインでも発生する可能性があります。

28
CharithJ

さらに、他の人がここで言及したことは、Application.DispatcherUnhandledException(およびその similars )と

<configuration>
  <runtime>  
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

app.configで、セカンダリスレッドの例外がアプリケーションをシャットダウンするのを防ぎます。

17

NLogを使用した完全な例

using NLog;
using System;
using System.Windows;

namespace MyApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public App()
        {
            var currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = (Exception)e.ExceptionObject;
            logger.Error("UnhandledException caught : " + ex.Message);
            logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
            logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
        }        
    }


}
1
Developer