web-dev-qa-db-ja.com

BackgroundWorkerの未処理の例外

BackgroundWorkerオブジェクトを利用して長時間実行される操作を実行する小さなWinFormsアプリがあります。

バックグラウンド操作は、通常、誰かが再作成中のファイルを開いているときに、例外をスローします。

コードがIDEから実行されるかどうかに関係なく、.NETは、未処理の例外が発生したことをユーザーに通知するエラーダイアログを表示します。リリース構成を使用してコードをコンパイルしても、これは変わりませんどちらか。

[〜#〜] msdn [〜#〜] によると:

操作でコードが処理できない例外が発生した場合、BackgroundWorkerは例外をキャッチしてRunWorkerCompletedイベントハンドラーに渡し、System.ComponentModel .. ::。RunWorkerCompletedEventArgsのErrorプロパティとして公開されます。 Visual Studioデバッガーで実行している場合、デバッガーは、未処理の例外が発生したDoWorkイベントハンドラーでブレークします。

私はこれらの例外が時々スローされることを期待しており、DoWorkではなくRunWorkerCompletedイベントでそれらを処理したいと考えています。私のコードは適切に動作し、エラーはRunWorkerCompletedイベント内で正しく処理されますが、「未処理の例外」が発生することを訴える.NETエラーダイアログを停止する方法を理解することはできません。

BackgroundWorkerはそのエラーを自動的にキャッチすることになっていないのですか?それは、MSDNのドキュメントに記載されていることではありませんか?このエラーisが処理されていることを.NETに通知し、RunWorkerCompletedEventArgsのErrorプロパティに例外をプロページすることを許可するには、何をする必要がありますか?

65
Andy

説明しているのは、BackgroundWorkerの定義された動作ではありません。あなたは何か間違ったことをしていると思う。

BackgroundWorkerがDoWorkで例外を食べ、RunWorkerCompletedで利用できるようにすることを証明する小さなサンプルを次に示します。

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) => 
    { 
        throw new InvalidOperationException("oh shiznit!"); 
    };
worker.RunWorkerCompleted += (sender, e) =>
    {
        if(e.Error != null)
        {
            MessageBox.Show("There was an error! " + e.Error.ToString());
        }
    };
worker.RunWorkerAsync();

私の精神的なデバッグスキルはあなたに問題を明らかにしています:RunWorkerCompletedハンドラーでe.Resultにアクセスしています-e.Errorがある場合、e.Resultにアクセスせずにそれを処理する必要があります。たとえば、次のコードは不良、不良、不良であり、実行時に例外をスローします。

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) => 
    { 
        throw new InvalidOperationException("oh shiznit!"); 
    };
worker.RunWorkerCompleted += (sender, e) =>
    {
        // OH NOOOOOOOES! Runtime exception, you can't access e.Result if there's an
        // error. You can check for errors using e.Error.
        var result = e.Result; 
    };
worker.RunWorkerAsync();

RunWorkerCompletedイベントハンドラーの適切な実装を次に示します。

private void RunWorkerCompletedHandler(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
       DoSomethingWith(e.Result); // Access e.Result only if no error occurred.
    }
}

VOILA、ランタイム例外は発生しません。

121

MSDNテキスト に追加します。

操作でコードが処理できない例外が発生した場合、BackgroundWorkerは例外をキャッチしてRunWorkerCompletedイベントハンドラーに渡し、System.ComponentModel .. ::。RunWorkerCompletedEventArgsのErrorプロパティとして公開されます。 Visual Studioデバッガーで実行している場合、デバッガーは、未処理の例外が発生したDoWorkイベントハンドラーのポイントで中断します。

... ANDデバッガーは例外を「〜Exceptionはユーザーコードによって処理されませんでした」と報告します

解決策:デバッガーの下で実行しないでください。期待どおりに動作します:e.Errorで例外がキャッチされました。

36
Mark Cranness

これは古い質問ですが、同じ症状をグーグルで調べているときに見つけました。他の誰かが同じ理由で見つけた場合にこれを投稿します。

ユダの答えは正しいのですが、「ユーザーコードの未処理の例外」ダイアログが表示される唯一の理由ではありません。バックグラウンドスレッドでコンストラクター内から例外がスローされると、その例外はすぐにダイアログを引き起こし、RunWorkerCompletedイベントに渡されません。問題のコードを任意のコンストラクターの外部(他のメソッド)に移動すると、期待どおりに機能します。

2
Rich

[編集]

ユダには大きなポイントがあります。私の例では、エラー処理の詳細を指摘しましたが、DoWorkメソッドで例外がヒットしなかった場合、コードは実際に別の例外を引き起こします。 BackgroundWorkerのエラー処理機能を具体的に示しているため、この例は問題ありません。ただし、エラーパラメータをnullに対してチェックしていない場合、これが問題になる可能性があります。

[/編集]

同じ結果は見られません。小さなコードを投稿できますか?これが私のコードです。

private void Form1_Load(object sender, EventArgs e)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.RunWorkerAsync();
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Will cause another exception if an exception didn't occur.
    // We should be checking to see if e.Error is not "null".
    textBox1.Text = "Error? " + e.Error;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < 10; i++)
    {
        if (i < 5)
        {
            Thread.Sleep(100);
        }
        else
        {
            throw new Exception("BOOM");
        }   
    }
}

プログラム出力:

エラー? System.Exception:BOOM in BackgroundException.Form1.worker_DoWork(Object sender、DoWorkEventArgs e)in D:\ Workspaces\Sandbox\BackgroundException\BackgroundException\Form1.cs:line 43 at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)at System .ComponentModel.BackgroundWorker.WorkerThreadStart(オブジェクト引数)

あなたの質問に似ている興味深い記事。例外処理に関するセクションがあります。

http://www.developerdotstar.com/community/node/671

1
Bobby Cannon

私は同じ問題を抱えていたので、グーグルでこのトピックを見つける前に、すでにユダの答えを適用していました。

さて、ユダの答えは部分的に正しいです。より良い答えを見つけました こちら

デバッガーはうまく機能しています。アプリケーションを「実際の条件」で実行すると、RunWorkerCompletedは例外を予想通りに処理し、アプリケーションの動作も予想どおりになります。

この答えがお役に立てば幸いです。

0
Cavaleiro