所有 GUI 线程的 Application.ThreadException 事件

本文关键字:ThreadException 事件 Application GUI 线程 所有 | 更新日期: 2023-09-27 18:34:29

我有一个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);
}

表格1.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();
}

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

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

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

所有 GUI 线程的 Application.ThreadException 事件

有没有另一种方法可以实现这一点,而无需重新注册 对于应用程序.线程异常事件,每次一个新的 GUI 线程 被创建?

我不知道,我和我的队友花了很多时间研究它。 .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)
    {
        // ...
    }
}