14

例外をスローし、Application.ThreadExceptionイベント ハンドラーで例外をキャッチするときに、奇妙な動作が見られます。

基本的に、以下のサンプルで起こっていることは、 のDoWorkイベント ハンドラーで例外がスローされることBackgroundWorkerです。RunWorkerCompletedイベント ハンドラーは、元の例外を内部例外として、新しい例外を再スローします。

内部例外がThreadExceptionイベント ハンドラーに表示され、実際の例外がスローされないのはなぜですか? RunWorkerCompletedイベント ハンドラーで内部例外を提供しない場合、正しい例外が表示されます。

using System;
using System.Windows.Forms;
using System.ComponentModel;

namespace WierdExceptionApp
{
    class WierdExceptionForm : Form
    {
        BackgroundWorker worker = new BackgroundWorker();

        public WierdExceptionForm()
        {
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            worker.RunWorkerAsync();
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw new Exception("worker_RunWorkerCompleted", e.Error);
            }
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            throw new Exception("worker_DoWork");
        }

        [STAThread]
        static void Main()
        {
            Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
            Application.Run(new WierdExceptionForm());
        }

        static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.Message);
        }
   }
}
4

2 に答える 2

12

RunWorkerCompleted イベントは、Control.Invoke() を機能させる WF 配管によって、BGW スレッドから UI スレッドにマーシャリングされます。基本的に、メッセージ ループによって空にされるデリゲートを含むキューがあります。これを行うコード Control.InvokeMarshaledCallbacks() は、コール スタックに表示されますが、未処理の例外をキャッチするための catch (Exception) 句があります。この句は Application.OnThreadException を呼び出し、Exception.GetBaseException() の値を渡します。

これで、内部例外のみが表示される理由が説明されます。なぜこのように行われるのかは少し不明です。実際の例外はバックグラウンド スレッドから発生したため、そうでなければかなり混乱する UI スレッドのコードのスタック フレームを切り取る可能性があります。

于 2008-12-07T21:33:50.607 に答える
1
        if (e.Error != null)
        {
            throw new Exception("worker_RunWorkerCompleted", new Exception("Inner", new Exception("Inner inner")));
        }

最後に「インナーインナー」を手に入れます。これが一番内側の例外を見る Application_ThreadException メソッドの振る舞いのようです。

于 2008-12-07T19:08:31.680 に答える