【问题标题】:Wierd C# exception behaviour: exception not caught in special circumstances奇怪的 C# 异常行为:在特殊情况下未捕获异常
【发布时间】:2020-06-02 16:07:20
【问题描述】:

我正在编写一个 dll 作为程序的扩展(Windows 10、Visual Studio Professional 2019)。在这个 dll 中,我有一个非常奇怪的行为。场景: 从那时起 dll 的“主”模块(应用程序的回调所在的位置)我打开一个对话框窗口(自己的模块),它在“显示表单”事件中调用第三个模块中的函数。在这第三个模块中,我抛出了一个异常(因为用户取消了一个操作),我想在“主”模块中捕获它。

当我在 VisualStudio 调试器中运行此 dll(调试或发布配置,没有区别)时,应用程序已启动,dll 已加载,上述过程就像一个魅力。 当我在没有 VisualStudio 的情况下运行应用程序时,应用程序已启动,dll 已加载,但在抛出异常后它没有像以前那样被捕获,系统告诉我抛出了异常但没有被捕获。

那么为什么代码可以在 IDE 环境中运行,而不是“独立”?

我唯一能想到的是 .NET 的事件处理代码存在差异。当我在“显示”事件函数中停止时,在调用堆栈中,我看到有对“mscorlib.dll!System.Threading.ExecutionContext”的调用。是否有可能在 VS 之外,dll 处于另一个执行上下文中,从而导致异常被误导?

如果有人可以向我解释这种行为,我将不胜感激。同样,这不是调试或发布版本的问题,而是关于执行环境的问题。

提前致谢,

约尔格

编辑

我希望这可以清除场景:

dll.cs:

// This function is called from the host application
void callback()
{
    try
    {
        Form dialog = new Form();
        dialog.ShowDialog();
    }
    catch (ExceptionType1)
    {
        ...
    }
    catch (ExceptionType2)
    {
        ...
    }
    catch
    {
        ...
    }
}

dialog.cs

...
// event function for the 'shown' event of Form class
private void SynchronizationDialog_Shown(object sender, EventArgs e)
{
    HelperFunction();
}
...

helper.cs

// here the exception is thrown
void HelperFunction()
{
    ...
    if(...)
    {
        throw new ExceptionType1();
    }
    ...
}

在VS中调试dll时,helper.cs中抛出的异常被catch块捕获,符合预期。 运行应用程序独立调用 dll 时,不会捕获到异常。

编辑

我现在可以提供一个示例项目(VS2020):ExceptionCatchTest

如果你在 VS 中运行它,异常会被捕获,否则不会。

【问题讨论】:

  • 实际查看您的代码会更有帮助。你的 try-catch 块在哪里? Form_Shown 事件是通过 Windows 消息触发的,因此在此事件中引发的异常不一定会被构造函数/show 周围的 try-catch 捕获。
  • 很遗憾,我无法在此处发布有用的代码,因为这不是我的财产,抱歉。你写道:“不一定会被抓住”。你能解释一下吗?或者给我一个提示在哪里寻找?
  • 表单使用 windows 消息来触发事件,例如表单加载、显示、鼠标移动、关闭和关闭。我的猜测是,当您处于调试模式时,Visual Studio 会处理这些消息并封装它们,使您能够在主代码中捕获异常。编译时会删除此封装,因此 try-catch 不再围绕触发异常。如果您可以将异常从表单显示事件移动到表单构造函数(而不是表单加载事件),那么您可以通过在 Form form = new Form() 创建表单周围放置一个 try-catch 来捕获异常。
  • @jokey 你不需要上传源代码。只需提供一个重现问题的简单演示即可。
  • @Adam:我想我没有清楚地描述这个场景。我用示例代码发表了一篇新文章。但原则上我认为你是对的。在我的项目中,我通过将 try-catch-blocks 移入表单模块(进入事件函数)解决了这种情况。因此,它必须对 VS 和独立应用程序之间的事件处理有所不同。所以我们有一个有根据的猜测,但我希望能找到能解释背景的人。

标签: c# .net visual-studio exception


【解决方案1】:

我使用您的代码创建了一个测试项目,并且能够重现您的问题。查看异常的堆栈跟踪,似乎窗体的 Windows 消息处理器 Control.WndProc 仅在从 Visual Studio 运行时位于堆栈中。作为测试,我覆盖了表单中的 WndProc 方法,在对基方法的调用周围添加了一个 try-catch 块

        protected override void WndProc(ref Message m)
        {
            try
            {
                base.WndProc(ref m);
            }
            catch
            {
                MessageBox.Show("WndProc caught");
                throw;
            }
        }

从 Visual Studio 运行时,会出现一个消息框,指示“WndProc 已捕获”。当不在 Visual Studio 中运行时,此消息在程序崩溃之前永远不会出现。

考虑一下,解决问题的最佳方法可能是添加一个公开可见的枚举,例如 public ErrorStatus errorStatus = ErrorStatus.NoError;。然后,您可以使用以下代码,而不是抛出异常和异常

errorStatus = ErrorStatus.Error1;
DialogResult = DialogResult.Abort;
return;

然后您可以将调用函数更改为

void callback()
{
    using (Form dialog = new Form())
    {
        DialogResult result = dialog.ShowDialog();
        if (result == DialogResult.Abort)
        {
            switch (dialog.errorStatus)
            {
                case ErrorStatus.Error1:
                    ...
                    break;
                case ErrorStatus.Error2:
                    ...
                    break;
            }
        }
    }
}

【讨论】:

  • 如果你不想使用枚举,你可以直接传递异常:public Exception errorStatus = null; 这允许 HelperFunction 在你的 Shown 事件中抛出带有try { HelperFunction(); } catch (Exception ex) { errorStatus = ex; DialogResult = DialogResult.Abort; } 的异常。 callback 然后可以处理 errorStatus 如果它不为空。
  • 感谢您了解发生这种情况的原因。但这是否意味着,在独立应用程序中,来自子表单的消息循环不会被处理,而是被重定向到主窗口消息循环?您提供的解决方案可能有效(我相信它确实有效),但我通过将 try-catch 块移动到表单中解决了这个问题。那更容易:-)。而且我认为它基本上是比以前更好的设计。所以作为一个经验法则,我会认为永远不要尝试在 C# 中将异常从一种形式传递到另一种形式。感谢大家的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 2011-08-25
  • 2014-04-12
  • 1970-01-01
  • 2014-07-17
  • 2019-11-19
  • 1970-01-01
相关资源
最近更新 更多