0

私のアプリケーション (.NET 4.0) では、カスタム テンプレートによるエラー レポートに smartassembly を使用しています。2 つのハンドラーをインストールします。

  1. グローバル例外キャッチャーをインストールし、例外が発生した場合はカスタム コードを呼び出します。そこで、例外の詳細を表示し、ユーザーがインターネット経由でデータを送信できるようにする WPF ウィンドウを表示します。
  2. #1 で処理できない例外が発生した場合、致命的な例外ハンドラを呼び出します。そこで、メッセージ ボックスに例外データを出力します。

ある顧客のマシン (Windows XP、.NET 4.0) で、アプリケーションの起動後に #2 からエラー メッセージが表示されました。その後、アプリケーションは終了します:

System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
  at System.Windows.Threading.Dispatcher.VerifyAccess()
  at Exapt.ErrorReporting.ErrorReportView..ctor()
  at Exapt.ErrorReporting.ExaptUnhandledExceptionHandler.OnReportException(ReportExceptionEventArgs e)
  at SmartAssembly.SmartExceptionsCore.UnhandledExceptionHandler.ReportException(Exception exception, Boolean canContinue, Boolean manuallyReported)

関連するコード:

public ExaptUnhandledExceptionHandler : UnhandledExceptionHandler
{
    protected override void OnReportException(ReportExceptionEventArgs e)
    {
        var view = new ErrorReportView();
        view.DataContext = new ErrorReportViewModel(this, e, view);

        view.ShowDialog();
    }
}

public ErrorReportView : Window
{
    public ErrorReportView()
    {
        this.InitializeComponent();

        // EDIT
        if (Application.Current != null)
            this.Owner = Application.Current.MainWindow;
        // END EDIT
    }
}

したがって、次のことが起こります。

  1. 起動中に例外が発生します (残念ながら、これは失われます)。
  2. 例外を処理するために、smartassembly はハンドラー 1 の OnReportException() を呼び出します。
  3. そこで、新しい ErrorReportView を作成します。
  4. WPF は、コンストラクター (前) でクロススレッド例外をスローしますInitializeComponent()!
  5. 例外処理中に例外が発生したため、smartassembly はハンドラー #2 を呼び出し、アプリケーションを終了します。

単純な new Window() がそれ自体でクロススレッド例外を引き起こす可能性があるのはどうしてですか?

4

3 に答える 3

2

ErrorReportViewWPFを使用して作成してみてくださいDispatcher

public ExaptUnhandledExceptionHandler : UnhandledExceptionHandler
{
    protected override void OnReportException(ReportExceptionEventArgs e)
    {
        Application.Current.Dispatcher.Invoke(new Action(() => 
        {
            var view = new ErrorReportView();
            view.DataContext = new ErrorReportViewModel(this, e, view);
            view.ShowDialog();
        }));
    }
}

テストも問題の再現もできないため、機能するかどうかはわかりませんが、試してみる価値はあります。

于 2012-11-20T12:28:38.973 に答える
1

オプションは、このレポートを処理する専用スレッドを起動することです。次のようになります。

[TestMethod]
public void TestMethod1()
{
    MainWindow window = null;

    // The dispatcher thread
    var t = new Thread(() =>
    {
        window = new MainWindow();

        // Initiates the dispatcher thread shutdown when the window closes
        window.Closed += (s, e) => window.Dispatcher.InvokeShutdown();

        window.Show();

        // Makes the thread support message pumping
        System.Windows.Threading.Dispatcher.Run();
    });

    // Configure the thread
    t.SetApartmentState(ApartmentState.STA);
    t.Start();
    t.Join();
}

ご了承ください:

  • ウィンドウは、新しいスレッド内に作成して表示する必要があります。
  • ThreadStart が戻る前に、ディスパッチャー (System.Windows.Threading.Dispatcher.Run()) を開始する必要があります。そうしないと、ウィンドウが表示されてすぐに終了します。
  • STA アパートメントで実行するようにスレッドを構成する必要があります。

このリンクで追加情報を見つけることができます。

于 2012-11-20T13:42:20.023 に答える
0

Arthur Nunes の回答と Sisyphe の回答により、すべての可能性を処理できるようになりました。STA スレッドで例外がスローされているようですが、そのスレッドはメイン (UI) スレッドではありません。おそらく JIT の最適化が原因で、取得したスタック トレースは少し不完全で、間違った場所で例外が発生していることを示しています。

固定コード:

public ExaptUnhandledExceptionHandler : UnhandledExceptionHandler
{
    protected override void OnReportException(ReportExceptionEventArgs e)
    {
        // Create a new STA thread if the current thread is not STA.
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
        {
            this.ShowErrorReportView(e);
        }
        else
        {
            // Since I use ShowDialog() below, there is no need for Dispatcher.Run()
            // or Dispatcher.InvokeShutdown()
            var thread = new Thread(() => this.ShowErrorReportView(e));
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
        }
    }

    private void ShowErrorReportView(ReportExceptionEventArgs e)
    {
        var view = new ErrorReportView();
        view.DataContext = new ErrorReportViewModel(this, e, view);

        view.ShowDialog();
    }
}

public ErrorReportView : Window
{
    public ErrorReportView()
    {
        this.InitializeComponent();

        // All of these cause accessing the MainWindow property or setting the Owner
        // to throw an exception.
        if (Application.Current != null
            && Application.Current.Dispatcher.CheckAccess()
            && Application.Current.MainWindow != null
            && Application.Current.MainWindow != this
            && Application.Current.MainWindow.IsLoaded)
        {
            this.Owner = Application.Current.MainWindow;
        }
    }
}
于 2012-11-20T14:29:40.830 に答える