C# 所有GUI线程的Application.ThreadException事件
我有一个WinForms应用程序,它可以创建多个表单,每个表单都在自己的GUI线程中(而不是主GUI线程)。我想为所有这些表单处理未处理的异常事件(Application.ThreadException),以处理任何错误。我还想处理来自工作线程的异常-这一点似乎工作正常,但我仍然无法处理来自GUI线程的异常: Program.cs:C# 所有GUI线程的Application.ThreadException事件,c#,winforms,exception-handling,C#,Winforms,Exception Handling,我有一个WinForms应用程序,它可以创建多个表单,每个表单都在自己的GUI线程中(而不是主GUI线程)。我想为所有这些表单处理未处理的异常事件(Application.ThreadException),以处理任何错误。我还想处理来自工作线程的异常-这一点似乎工作正常,但我仍然无法处理来自GUI线程的异常: Program.cs: [STAThread] static void Main() { AttachExceptionHandlers(); Application.Enable
[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();
}
此代码将在前两个实例(GUI线程崩溃和工作线程崩溃)中调用我的异常处理程序,但不处理创建新GUI线程的第三个实例。我发现,如果调用Program.AttachExceptionHandlers();在Application.Run(form)行之前,一切正常,但这是不可取的,因为我必须实现一些逻辑来确保在每个线程上创建表单之前调用Program.AttachExceptionHandlers()(如果在线程上创建表单之后调用Application.SetUnhandledExceptionMode,则调用Application.SetUnhandledExceptionMode失败)
这个例子是一个更大的代码的一部分,理想情况下,它会给我的代码的用户一个简单的API,让他们在应用程序开始时调用(比如Program.cs),以附加异常处理程序。然后,异常处理程序执行一些神奇的操作来记录在应用程序死亡之前抛出的异常的详细信息。因此,告诉用户他们必须在每次创建新的GUI线程(工作线程似乎不受此问题影响)并重新连接应用程序时跟踪。ThreadException处理程序不是一个干净的解决方案
是否有其他方法可以实现这一点,而无需在每次创建新GUI线程时重新注册Application.ThreadException事件
有没有其他方法可以实现这一点,而无需重新注册
对于Application.ThreadException事件,每次新的GUI线程
创造了什么
我不知道有什么,我的队友和我都花了很长时间来研究它。NET WinForms在如何创建/管理/销毁多个消息泵的问题上似乎不是很固执己见
除了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)
{
// ...
}
}
使用系统;
使用系统线程;
使用System.Windows.Forms;
内部静态类程序
{
[状态线程]
私有静态void Main()
{
CreateFormAndStartMessagePump(()=>CreateForm(“第一”),OneException,OneException,false,“pumpThread1”);
CreateFormAndStartMessagePump(()=>CreateForm(“第二”),OneException,OneException,false,“pumpThread2”);
//注意:此处未处理应用程序关闭
}
专用静态T Createform和StartMessagePump(
Func createForm,
ThreadExceptionEventHandler onThreadException,
未处理的异常Venthandler onDomainException,
布尔是背景,
字符串名称),其中T:Form
{
var闩锁=新手动重置事件(错误);
T form=null;
变量线程=新线程(ts=>
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException+=onThreadException;
AppDomain.CurrentDomain.UnhandledException+=onDomainException;
form=createForm();
lock.Set();
Application.Run();
})
{
IsBackground=IsBackground,
Name=Name
};
SetApartmentState(ApartmentState.STA);
thread.Start();
latch.WaitOne();
申报表;
}
私有静态表单CreateForm(字符串名称)
{
var form=new form();
form.Text=名称;
form.Show();
申报表;
}
私有静态void-OnException(对象发送方,未处理的异常ventargs e)
{
// ...
}
私有静态void-OnException(对象发送方,ThreadExceptionEventArgs e)
{
// ...
}
}
你根本不知道自己陷入了什么境地。抱怨一个方法调用是非常愚蠢的,因为创建一个多线程GUI需要做大量的工作。别这么做,好吧,公平点。我的问题不清楚的是,我实际上只是提供了一个简单的库,它包含在其他应用程序中,用于处理未处理的异常。因此,我实际上无法控制在目标应用程序中创建的GUI线程的数量,也无法控制我的库的用户将要做的任何其他事情。因此,我不想强迫他们在每次创建新的GUI线程时进行额外调用。上面的链接似乎解释了消息泵等。每个GUI线程都有自己的消息泵,因此这种死锁不是问题。“一个WinForms应用程序,创建多个表单,每个表单都在自己的GUI线程中”——这就是您的问题所在。至于“我不想强迫他们在每次创建一个新的GUI线程时都打一个额外的电话”,你对你的客户太随和了。如果你可以通过强迫他们对每个新的UI线程进行额外的调用来解决这个问题,那么就这么做吧。不要浪费任何时间,让滥用框架的人的生活更轻松。@HansPassant在手机都有多个内核的时代,我认为在GUI中有多个线程和消息泵是合理的。尽管我同意,除非你有令人信服的理由,否则你不应该使用多个消息泵。用户界面线程需要做的就是让人的眼睛被占据,并对人的手保持反应。这非常简单,人类时间比处理器时间慢7个数量级。如果单处理器核心无法做到这一点,那么就错了。