托管的C#用户控件在MFC对话框中使用时出现未处理的异常

3
我们的核心应用程序是使用MFC C++构建的,但我们正在尝试在.NET中编写新代码,并创建了一个.NET用户控件,该控件将在现有的MFC Dialog上使用。
然而,当从用户控件抛出未预期/未处理的异常时,它会导致MFC应用程序崩溃(非法操作风格),而且没有恢复的能力。
我已添加了

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(currentDomain_UnhandledException);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

针对.NET用户控件的构造函数,但似乎没有捕获到任何内容。

有没有办法在MFC中添加事件来处理这些内容?

快速搜索没有返回任何有用的结果。

谢谢。


编辑:仍然无法以我想要的方式解决这个问题,看起来最好的方法是在所有的.NET代码周围尝试和捕获,以便没有异常冒泡。


检查我的编辑。我在每个事件处理程序中都使用它,你也可以这样做。 - John Saunders
你只需要在.NET代码的最外层——事件处理程序和公共入口点周围尝试/捕获。 - John Saunders
哦,是的,那就是我必须做的,但它确实覆盖了所有.NET代码,直接或间接地。 - PostMan
4个回答

3

我一段时间前也问过同样的问题:在混合的本地/托管可执行文件中,最终的托管异常处理程序是什么?

我的发现是,在托管线程中运行时,托管未处理异常事件才会触发。托管的WndProc是魔法所在。

你有几个选择:你可以在CWinApp中放置一个低级别的覆盖,并捕获Exception^。这可能会产生意想不到的副作用。或者,你可以选择结构化异常处理(SEH),它将为你提供对所有未处理异常的入侵。这不是一条容易的路。


0
当然,更好的想法是在C#代码中处理异常,或者找出导致异常的原因,并修复它,以便不会抛出异常。是什么阻止了这一点?
在用户控件的每个事件处理程序中放置一个try/catch块:
private void btnOk_Click(object sender, EventArgs e) {
    try {
        // real code here
    }
    catch (Exception ex) {
        LogException(ex);
        // do whatever you must in order to shut down the control, maybe just
    }
}

这是我使用的:

public static class UI
{
    public static void PerformUIAction(Control form, Action action)
    {
        PerformUIAction(form, action, (string) null);
    }

    public static void PerformUIAction(
        Control form, Action action, string message)
    {
        PerformUIAction(form, action, () => message);
    }

    public static void PerformUIAction(
        Control form, Action action, Func<string> messageHandler)
    {
        var saveCursor = form.Cursor;
        form.Cursor = Cursors.WaitCursor;
        try
        {
            action();
        }
        catch (Exception ex)
        {
            MessageBox.Show(
                messageHandler() ?? ex.Message, "Exception!",
                MessageBoxButtons.OK, MessageBoxIcon.Error,
                MessageBoxDefaultButton.Button1,
                MessageBoxOptions.DefaultDesktopOnly);
            Debug.WriteLine(ex.ToString(), "Exception");
            throw;
        }
        finally
        {
            form.Cursor = saveCursor;
        }
    }
}

我这样称呼它:

private void _copyButton_Click(object sender, EventArgs e)
{
    UI.PerformUIAction(
        this, () =>
                  {
                  // Your code here
                  });
}

关于异常的问题在于我们不知道它们何时出现,或者为什么会出现。所以这是不可能的。 而且用户控件有相当多的事件和方法。 我已经编写了针对可能出现的异常的代码,但我想捕获其他异常。 - PostMan
似乎需要做很多工作,而且我真的需要一个解决方案,不需要我在每个需要捕获异常的地方添加额外的代码(除了我可以预期的异常)。 - PostMan
随你喜欢。但请注意,在打开“{”后添加三行代码和在结尾“}”前添加一行代码。如果您不介意使用一些简单的正则表达式,可以使用搜索和替换来完成此操作。另外,您可能没有注意到:您可以在“your code here”块内访问“sender”和“e”。您包装的代码不会改变。 - John Saunders
不要误解我,这是一个很好的解决方案,但可惜并不适合我的问题。如果我能够发布更多的代码,我会的,但由于它涉及工作等原因,我不能这样做。 - PostMan
没问题。我没有被冒犯。 :-) 我很想知道在什么情况下这个模式不适用,因为多年来它已经适用于许多情况。我最初在 .NET 1.1 中使用它,当时必须为委托使用显式方法名称。那真的很丑陋!自从2003年以来,Lambda解决了我所有的问题! - John Saunders

0
我在.NET用户控件的构造函数中添加了未处理异常处理程序。
我认为那样不起作用;我相信异常处理程序必须在调用 Application.Run 之前设置。您的应用程序中有任何 Application.Run 调用吗?
在初始化任何.NET控件之前,您在MFC方面尝试这样的操作如何:
Application.SetUnhandledExceptionMode(System.Windows.Forms.UnhandledExceptionMode.CatchException);
Application.ThreadException += ...;

查看是否调用了ThreadException处理程序。


有趣的想法,请尝试后分享结果。 - Aidan Ryan
我应该让线程异常调用.NET事件还是MFC代码?抱歉,我对编程一窍不通... - PostMan
将其放在MFC方面处理。您需要将C++代码编译为C++/CLI(托管的C++代码)。 - Judah Gabriel Himango

0

我认为你想在MFC方面设置未处理异常处理程序:

AppDomain::CurrentDomain->UnhandledException += gcnew UnhandledExceptionEventHandler(&CurrentDomain_UnhandledException);

[Application.ThreadException同理]

请看一下MSDN论坛上类似的问题:混合模式应用程序中的未处理异常


提供的链接中的代码实际上并不起作用,提供的示例也很遗憾地无法运行。编译成功,但结果仍然相同。 - PostMan
确实,这不起作用。另外,为什么事件注册运行的上下文会对注册产生任何影响呢?您链接的线程有一个错误答案被接受,后来的回复证实了提问者的问题。 - Aidan Ryan

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