捕获另一个表单抛出的异常

Posted

技术标签:

【中文标题】捕获另一个表单抛出的异常【英文标题】:Catch an Exception thrown by another form 【发布时间】:2012-01-12 22:35:22 【问题描述】:

我正在尝试这样做:

我正在创建另一个表单,它的 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();

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

用户代码未处理异常

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

【问题讨论】:

请不要将C#放在标题中。这就是标签的用途。 我不明白你描述的问题。我认为问题确实存在,因为 frmOptions 在不同的线程上运行,但显然情况并非如此。我能够捕捉到没有问题的异常。我针对 .NET 2 和 4 进行了测试。 @Icarus:转到调试器 -> 异常 -> 全部重置。然后再试一次,你会得到用户代码未处理的异常。 @makmiler:做到了。相同的非问题。 @Icarus 好吧,我使用 VS 2010 和 .net 4,所以它发生了。其他人也有同样的错误。为什么你没有看到消息,我真的不知道。 【参考方案1】:

我认为这行不通,新表单没有在您上面的代码上下文中运行,它只是由它启动。如果您检查堆栈跟踪中抛出的异常,您不应该在其中看到上面的代码,因此它无法捕获异常。

更新:我刚刚创建了一个测试项目并进行了尝试。 stacktrace 对原始表单一无所知。如果您想捕获未处理的异常,您可能需要查看这个问题Unhandled Exception Handler in .NET 1.1

【讨论】:

“它只是被它启动”是什么意思?对象是在创建它的表单的对象空间中创建的。如果我打开“不要在未处理的异常上停止”,则异常被捕获在主窗体的 try/catch 块中。但这也是一种“托管”异常处理。问题是为什么调试器认为它没有问题? 正如 Renato 在对他们的回答的评论中提到的那样,新表单在单独的 UI 线程中运行,即使它是以模态方式启动的。 frmOptions 中的错误在其堆栈跟踪中没有对启动表单的任何引用 那么您将得到 ThreadException,而不是未处理的用户代码异常。我不相信是这样的,因为如果你在未处理的用户代码上关闭 debugger-Exceptions-Stop,主窗体的 try/catch 块实际上会捕获异常。 @makmiler 我做不到,异常不会靠近调用表单【参考方案2】:

您可以从 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");
            
        

【讨论】:

谢谢你,但是,这不是我要问的。【参考方案3】:

您可以按以下方式执行此操作:

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

【讨论】:

是的,不错的技巧,但不是。这不是这里的问题。请再次阅读我的问题:) makmiler,碰巧您的第二个表单在不同的 ui 线程上下文中运行,这就是您无法直接捕获此异常的原因,这就是 Glenn Slaven 的回答非常有意义的原因。 @this.__curious_geek,你为什么不推荐它? @Renato Gama,如果是这样,那为什么在未处理的用户代码上关闭 debugger-Exceptions-Stop,主窗体的 try/catch 块实际上会捕获异常?跨度> @makmiler,我无法重现您的步骤!你到底做了什么?【参考方案4】:

您为什么要尝试从一种形式向另一种形式抛出异常? "Don't throw new Exception()"

如果您想让主表单知道选项表单已关闭,您可以在主表单上设置一个从选项表单设置的标志。

【讨论】:

我知道。我在问为什么调试器作为未处理的用户代码停止在异常上,事实上,表单的创建是在 try/catch 块中,如果您选择在此类用户代码上停止调试器,异常最终会被捕获。

以上是关于捕获另一个表单抛出的异常的主要内容,如果未能解决你的问题,请参考以下文章

捕获在不同线程中抛出的异常

捕获 AuthenticationProvider 中抛出的异常

控制器可以捕获模型抛出的异常吗?

如何捕获ctypes中抛出的异常?

捕获 token_get_all (Tokenizer) 抛出的错误

为啥不能在主线程中直接捕获和处理工作线程抛出的异常?