所有 GUI 线程的 Application.ThreadException 事件

rb_*_*rb_ 5 c# exception-handling winforms

我有一个 WinForms 应用程序,它创建了多个表单,每个表单都在自己的 GUI 线程中(不是主 GUI 线程)。我想为所有这些表单处理未处理的异常事件 (Application.ThreadException) 以处理任何错误。我还想处理来自工作线程的异常 - 这一点似乎工作正常,但我仍然遇到来自 GUI 线程的异常的问题:

程序.cs:

[STAThread]
static void Main()
{
  AttachExceptionHandlers();
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  Application.Run(new Form1());
}
public static void AttachExceptionHandlers()
{
  Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
  Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);      
  AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
  Dispatcher.CurrentDispatcher.UnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(CurrentDispatcher_UnhandledException);
}
Run Code Online (Sandbox Code Playgroud)

Form1.cs:

//GUI Thread Crash
private void button1_Click(object sender, EventArgs e)
{
  object a = null;
  a.ToString();
}
//Worker Thread Crash
private void button2_Click(object sender, EventArgs e)
{
  Thread myThread = new Thread(() => 
  { 
    object a = null; 
    a.ToString(); 
  });
  myThread.Start();
  myThread.Join();
}
//New Thread, New Gui Crash
private void button3_Click(object sender, EventArgs e)
{
  Thread myThread = new Thread(() => 
  { 
    using (CrashingForm form = new CrashingForm()) //Crashing Form Crashes in it's FormLoad event.
    {
      Application.Run(form);
    }
  });
  myThread.Start();
  myThread.Join();
}
Run Code Online (Sandbox Code Playgroud)

此代码将在前 2 个实例(GUI 线程崩溃和工作线程崩溃)中调用我的异常处理程序,但不处理创建新 GUI 线程的第三个实例。我发现如果我调用 Program.AttachExceptionHandlers(); 在 Application.Run(form) 行之前,一切正常,但这是不可取的,因为我必须实现一些逻辑以确保在我们调用每个线程上创建表单之前调用 Program.AttachExceptionHandlers() 。如果在线程上创建表单后调用,则对 Application.SetUnhandledExceptionMode 的调用将失败)。

此示例是较大代码段的一部分,理想情况下,该代码将为我的代码用户提供一个简单的 API,以便在他们的应用程序开始时调用(如在 Program.cs 中)以附加异常处理程序。然后,异常处理程序会执行一些魔术来记录有关在应用程序终止之前抛出的异常的详细信息。因此,告诉用户他们必须在每次创建新的 GUI 线程时进行跟踪(工作线程似乎不受此问题的影响)并重新附加 Application.ThreadException 处理程序并不是一个干净的解决方案。

是否有另一种方法可以实现这一点,而不必在每次创建新的 GUI 线程时为 Application.ThreadException 事件重新注册?

Gar*_*ith 2

是否有另一种方法可以实现此目的,而无需在每次创建新的 GUI 线程时重新注册 Application.ThreadException 事件?

我不知道有什么,我和我的队友花了很多时间来研究它。当谈到如何创建/管理/销毁多个消息泵时,.NET WinForms 似乎并没有非常固执己见。

除了Retlang的WinFormsFiber之外,我们还使用类似于下面的框架方法。

using System;
using System.Threading;
using System.Windows.Forms;

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        CreateFormAndStartMessagePump(() => CreateForm("first"), OnException, OnException, false, "pumpThread1");
        CreateFormAndStartMessagePump(() => CreateForm("second"), OnException, OnException, false, "pumpThread2");
        // note app shutdown not handled here
    }

    private static T CreateFormAndStartMessagePump<T>(
        Func<T> createForm,
        ThreadExceptionEventHandler onThreadException,
        UnhandledExceptionEventHandler onDomainException,
        bool isBackground,
        string name) where T : Form
    {
        var latch = new ManualResetEvent(false);
        T form = null;
        var thread = new Thread(ts =>
        {
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            Application.ThreadException += onThreadException;
            AppDomain.CurrentDomain.UnhandledException += onDomainException;
            form = createForm();
            latch.Set();
            Application.Run();
        })
        {
            IsBackground = isBackground,
            Name = name
        };
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        latch.WaitOne();
        return form;
    }

    private static Form CreateForm(string name)
    {
        var form = new Form();
        form.Text = name;
        form.Show();
        return form;
    }

    private static void OnException(object sender, UnhandledExceptionEventArgs e)
    {
        // ...
    }

    private static void OnException(object sender, ThreadExceptionEventArgs e)
    {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)