2012-11-20 27 views
0

在我的应用程序(.NET 4.0)中,我使用smartassembly进行错误报告并使用自定义模板。它安装了两个处理程序:创建WPF窗口时发生跨线程异常

  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并终止应用程序。

一个简单的新Window()如何可能导致自身的跨线程异常?

+0

我不知道smartAssembly知识,但它似乎OnReportException方法是在一个线程是不是UI胎面执行。您是否尝试通过Application.Current.Dispatcher.Invoke方法创建ErrorReportView?编辑:这是一个值得尝试的答案。 – Sisyphe

+0

不要太在AppDomain.UnhandledException的事件处理程序中花哨。它在遭受崩溃的线程上产生,因此创建一个WPF窗口不会起作用。保持简单,记录错误,并挤出一个MessageBox,不再。 –

+0

@HansPassant我必须有些奇特,因为数据通过互联网发送,我需要用户的同意。 –

回答

2

尝试使用WPF Dispatcher创建您ErrorReportView

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

正如我无法测试,或重现您的问题,我不知道它会工作,但它是值得一试。

+0

它可行,但这不是一件非常安全的事情。如果在UI线程上运行的代码引发异常,它可能很容易死锁。 –

+0

感谢您的意见。我想应该可以检测到异常被抛出的线程,并通过直接或通过调度器创建窗口来适应。我虽然没有给出很多这个问题,但不会再进一步​​;) – Sisyphe

+0

这让我想到了。我不幸遗漏了一些代码,也就是我设置窗口的所有者的地方。如果我从一个不同的STA线程创建一个异常,我会得到一个类似的异常(不完全如此,因为异常细节指向不同的位置,即在Application.Current.MainWindow调用)。 –

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公寓中运行。

你可以在this link找到更多的信息。

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

Gratz在解决你的问题队友! – Sisyphe