捕获另一个窗体抛出的异常

7

我想做这件事:

我正在创建另一个表单,其中在其FormClosed方法中抛出异常,该异常应由主表单捕获。

主表单:

try
    {
        frmOptions frm  = new frmOptions();
        frm.ShowDialog();                        
    }
catch(Exception)
    {
        MessageBox.Show("Exception caught.");
    }

frmOptions:

private void frmOptions_FormClosed(object sender, FormClosedEventArgs e)
{
    throw new Exception();
}

调试器停在异常处并显示以下消息:

用户代码未处理异常

为什么会这样?我在创建该异常对象的所有者中捕获了它。有人有想法吗?

1
请不要在标题中加入C#,这是标签的作用。 - user47589
我不明白你所描述的问题。我认为这个问题存在是因为frmOptions在不同的线程上运行,但显然并非如此。我能够轻松地捕获异常。我测试了.NET 2和4的目标。 - Icarus
@Icarus: 转到调试器 -> 异常 -> 重置全部。然后再试一次,你会得到用户代码未处理的异常。 - mileski
@makmiler:已经做了。同样没有问题。 - Icarus
@Icarus 嗯,我使用的是VS 2010和.NET 4,所以会出现这种情况。其他人也遇到了同样的错误。为什么你没有看到这个消息,我真的不知道。 - mileski
4个回答

3

你可以从program.cs处理项目中的所有异常。

static class Program
        {

            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException;

                Application.ThreadException += Application_ThreadException;
                Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);         
                Application.Run(new MainMDI());              
            }
            static void Application_ThreadException(Object sender, ThreadExceptionEventArgs e)
            {
                MessageBox.Show(e.Exception.Message, "Application.ThreadException");
            }

            static void AppDomain_UnhandledException(Object sender, UnhandledExceptionEventArgs e)
            {
                MessageBox.Show(((Exception)e.ExceptionObject).Message, "AppDomain.UnhandledException");
            }
        }

谢谢您的回答,但那不是我要问的。 - mileski

2
我认为这不会起作用,新表单并没有在上述代码的上下文中运行,而是仅由它启动。如果您检查抛出的异常的堆栈跟踪,您将不会在其中看到上面的代码,因此它无法捕获异常。
更新:我刚刚创建了一个测试项目并尝试了一下。堆栈跟踪与原始表单无关。如果您想捕获未处理的异常,您可能需要查看这个问题:.NET 1.1中的未处理异常处理程序

1
“它只是被它启动了”是什么意思?该对象是在创建它的窗体的对象空间中创建的。如果我关闭“不要停止未处理的异常”,则异常将被捕获在主窗体的try/catch块中。但这也是一种“托管”异常处理。问题是为什么调试器将其视为未处理的异常? - mileski
正如Renato在他们的回答评论中提到的那样,即使以模态方式启动,新表单也在单独的UI线程中运行。 frmOptions中的错误在其堆栈跟踪中没有任何对启动表单的引用。 - Glenn Slaven
如果您关闭调试器-异常-停止未处理的用户代码,那么您将得到ThreadException而不是未处理的用户代码异常。我不认为这是正确的,因为主窗体的try/catch块实际上捕获了异常。 - mileski
@makmiler,我无法让它做到那样,异常与调用表单没有任何关系。 - Glenn Slaven

2

您可以按照以下方式完成此操作:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 form2 = new Form2(this);
        form2.Show();
    }

    public void HandleForm2Exception(Exception ex)
    {
        MessageBox.Show("EXCEPTION HAPPENED!");
    }
}

并且在 Form2.cs 上

public partial class Form2 : Form
{
    private Form1 form1;

    public Form2(Form1 form1) : this()
    {
        this.form1 = form1;
    }

    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_FormClosed(object sender, FormClosedEventArgs e)
    {
        try
        {
            throw new Exception();
        }
        catch (Exception ex)
        {
            if(this.form1 != null)
                this.form1.HandleForm2Exception(ex);
        }
    }
}

是的,很巧妙的技巧,但不是问题所在。请再次阅读我的问题 :) - mileski
1
makmiler,发生这种情况是因为你的第二个表单在不同的UI线程上下文中运行,这就是为什么你不能直接捕获这个异常,也是Glenn Slaven的回答完全正确的原因。@this.__curious_geek,为什么你不推荐它? - Renato Gama
@Renato Gama,如果是这样的话,那么为什么如果关闭调试器-异常-停止未处理的用户代码,主窗体的try/catch块实际上会捕获异常呢? - mileski
@makmiler,我无法重现你的步骤!你具体做了什么? - Renato Gama

1
为什么你要试图从一个表单抛出异常到另一个表单呢? "不要抛出新的异常()" 如果你想让主表单知道选项表单已关闭,你可以在主表单上设置一个标记来表示它来自选项表单。

我知道这一点。我想问的是,为什么调试器会在异常作为未处理的用户代码时停止,事实上,窗体的创建确实在try/catch块中,如果你打开停止调试器在这种用户代码上的选项,最终异常将被捕获。 - mileski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接