有没有一种方法可以防止Visual Studio在特定方法中出现异常时中断?

18

我知道我可以通过“异常”对话框来控制Visual Studio处理不同类型异常以及它们是否被捕获。

然而,当我调用特定方法时,我使用的一个库在内部抛出(并捕获)了ArgumentOutOfRange异常。这个异常可能只有1%的几率会被抛出(并被库捕获),但我会频繁地调用这个方法。编辑器说这是设计上的需求(而且,他们选择的设计是有意义的)。

问题是我不想每次抛出该异常时Visual Studio都停下来。

  • 我不想停止在ArgumentOutOfRange异常处停顿,因为我可能在我的代码中也有这些异常,并希望在这些异常处停顿。
  • 我不想启用“仅限我的代码”调试,因为我关心我的代码之外抛出的异常(尤其是出于性能方面的原因)。

有办法实现吗? 我一直在研究属性(例如DebuggerStepThrough),但还没有找到合适的东西。

您有任何关于如何解决此问题的提示吗?


2
根据这个问题DebuggerStepThrough属性应该是你的好朋友。然而,看起来你可能无法直接修改第三方库... - Dirk Vollmar
这个问题的答案似乎表明这是不可能的。虽然我不确定如果你在外部代码引发异常时中断,你会完成什么任务。如果您没有修改源代码的能力,那么您如何解决它们?这是为什么使用异常进行流程控制真的是一个非常糟糕的实践的典型例子。这里有一个库设计师犯了完全相同的错误。 - Cody Gray
1
在 OzCode 中没有得到太多关注:http://ozcode.userecho.com/topics/362-add-feature-to-ignore-an-exception-thrown-breakpoint-on-a-specific-line-from-the-quickaction-menu/ - Thomas Weller
3个回答

3
我不想启用“仅限我的代码”调试功能。 是的,现在就停下来。这正是您需要避免不必要调试器中断的功能。如果您不想知道别人的烂代码,那么请将复选框切换回原来状态。 当程序员使用异常来控制流程时,这种情况往往会失控。这是一个非常常见的问题。要将其转化成将调试会话变成非常繁琐的单击噩梦,则需要两个。当您需要调试器功能以在第一次机会异常时中断时,如果其他人也需要该功能,则基本上已经输了。 每个人都希望他们可以神奇地使用[DebuggerNonUserCode]或[DebuggerHidden]或[DebuggerStepThrough]属性来使该问题消失。但它不会。另一个程序员并不认为他的代码不重要到足以获得这些属性。好吧,它确实没有,因为始终存在使用try / catch-em-all代码的代码中隐藏的漏洞。精灵宝可梦代码。 因此,Microsoft必须找到另一种方法来帮助程序员处理烂库代码。他们做到了。选择该复选框,轰隆,解决了。您无法对此烂代码做任何事情,除了向作者发送难看的邮件。不要让我们或Microsoft也减慢您这样做的速度,大家必须和睦相处,以创建一个人们喜欢使用的产品。

也许我没有表达清楚我的观点。这个情况并不是试图掩盖异常,而是使Visual Studio调试器默认表现得合理,而无需进行配置更改。以下是一个例子。我们有一个带有自定义日志记录的应用程序,解决方案中另一个项目的自定义日志记录部分具有将日志数据发布到HTTP端点的代码。当HTTP API失败时,会抛出异常,我们使用Polly来处理这些异常并重试,但由于我们拥有此代码,每次Visual Studio都会在该行上中断!在这种情况下,我们已经真正处理了异常。 - jpierson
在这种情况下,团队中的开发人员将继续注释掉日志记录代码,因为在 Visual Studio 中间歇性故障和烦人的异常处理中断。 - jpierson
我不太清楚你在说什么。Polly?为什么调试器在处理时会停止?我回答了被问到的问题,似乎与你的问题没有太大关系。考虑发布你自己的问题,在这个问题下发表评论以链接它。 - Hans Passant
2
@HansPassant Polly https://github.com/App-vNext/Polly 是一个异常和瞬态故障处理库,由Dot Net Foundation管理:https://dotnetfoundation.org/blog/welcome-polly - mountain traveller

2
我认为在Visual Studio中不可能实现,但在WinDbg中可以。例如,可以参考这篇文章
另外一方面,似乎从Visual Studio 2010开始,您可以直接加载和使用WinDbg扩展DLL,提供额外的功能(包括可能需要的功能),但我还没有尝试过。例如,可以参考这篇文章

1
你可以使用 Visual Studio(从2012版开始)内置的调试引擎Concord。它通过一个很好的托管API(并且可以使用vsix技术部署),可以进行扩展,但是没有完全记录。
Concord有调试监视器的概念,我们可以使用IDkmDebugMonitorExceptionNotification接口来挂钩。
这个接口可以监视抛出的所有异常。它还可以“抑制”检测到的任何异常事件,这正是我们需要的。
我建议从Hello World示例开始:下载并确保它按预期运行。
现在,只需像这样修改HelloWorld.vsdconfigxml

<!--TODO: If you copy the sample, ensure to regenerate the GUID in this file -->

<!-- 1. change component level to something higher than 40500 -->
<ManagedComponent
  ComponentId="51736b11-9fb4-4b6d-8aca-a10a2b7ae768"
  ComponentLevel="40501"
  AssemblyName="HelloWorld">

  <!-- 2. change class full name to HelloWorld.ExceptionHandler, for example -->
  <Class Name="HelloWorld.ExceptionHandler">
    <Implements>
      <InterfaceGroup>
        <NoFilter/>
        <!-- 3. change supported interface -->
        <Interface Name="IDkmDebugMonitorExceptionNotification"/>
      </InterfaceGroup>
    </Implements>
  </Class>

</ManagedComponent>

然后,只需创建一个名为 ExceptionHandler.cs 的类,并在其中放置以下内容:
public class ExceptionHandler : IDkmDebugMonitorExceptionNotification
{
    private bool _unhandledDetected;

    // we're being called!
    public void OnDebugMonitorException(DkmExceptionInformation exception, DkmWorkList workList, DkmEventDescriptorS eventDescriptor)
    {
        if (_unhandledDetected)
        {
            // this will cause the program to terminate
            eventDescriptor.Suppress();
            return;
        }

        if (exception.ProcessingStage.HasFlag(DkmExceptionProcessingStage.Unhandled))
        {
            _unhandledDetected = true;
        }
        else if (exception.ProcessingStage.HasFlag(DkmExceptionProcessingStage.Thrown))
        {
            if (SuppressException(exception))
            {
                eventDescriptor.Suppress();
            }
        }
    }

    // should we suppress a thrown (1st chance) exception?
    private bool SuppressException(DkmExceptionInformation exception)
    {
        // implement any custom logic in here, for example use the exception's name
        if (exception.Name == typeof(ArgumentOutOfRangeException).FullName)
        {
            // for example, use the module (assembly) name
            var clrAddress = (DkmClrInstructionAddress)exception.InstructionAddress;
            var clrModule = clrAddress.ModuleInstance;
            if (clrModule.Name == "TheUglyOne.dll")
                return true; // we don't want this one!
        }
        return false;
    }
}

当您运行项目时,您应该看到所有异常都被监视(不管您的“仅限我的代码”和/或异常触发器设置如何),所以您需要做的就是实现一些逻辑来抑制您真正不想看到的异常。我没有检查过,但我认为您可以使用自定义属性构建逻辑,因为Dkm类提供了相当多的.NET元数据信息。
注意:正如您所看到的,有一些技巧可以确保程序正常终止。

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