Есть ли способ предотвратить взлом Visual Studio исключений в конкретном методе? - PullRequest
12 голосов
/ 01 марта 2011

Я знаю, что могу контролировать, как Visual Studio обрабатывает исключения в соответствии с их типом и тем фактом, что они в конечном итоге перехватываются с помощью диалога «Исключение».

Однако у меня есть библиотека, которая внутренне выдает (и перехватывает) исключение ArgumentOutOfRange, когда я вызываю определенный метод. Исключение выдается (и перехватывается библиотекой), возможно, 1% времени, но я часто вызываю этот метод. Редактор говорит, что это дизайн (и действительно, дизайн, который они выбрали, имеет смысл).

Дело в том, что я не хочу, чтобы Visual Studio ломалась каждый раз, когда выдается исключение.

  • Я не хочу прекращать разбивать исключения ArgumentOutOfRange, так как у меня могут быть некоторые в моем коде и я хочу разбить их.
  • Я не хочу включать отладку «только мой код», потому что я обеспокоен исключениями, которые выбрасываются за пределы моего кода (особенно по соображениям производительности)

Есть ли способ достичь этого? Я изучал атрибуты (например, DebuggerStepThrough), но пока не нашел что-то подходящее.

Любые советы о том, как это сделать?

Ответы [ 3 ]

2 голосов
/ 03 декабря 2016

Я не хочу включать отладку "только мой код"

Да, остановись прямо сейчас. То есть точно функция, которая вам нужна, чтобы не получить нежелательных перерывов в отладчике. Если вы не хотите знать о чужом дрянном коде, установите этот флажок обратно.

Это неизменно сходит с рельсов, когда программисты используют исключения для управления потоком. Очень распространенное преступление. Требуется два из них, чтобы превратить это в беспорядок, который превращает сеанс отладки в очень нудный кошмарный клик. Когда вам нужна функция отладчика, которая нарушает исключение первого шанса, тогда вы в основном проигрываете, если кому-то это тоже нужно.

Все надеются, что они могут волшебным образом использовать атрибуты [DebuggerNonUserCode], [DebuggerHidden] или [DebuggerStepThrough], чтобы эта проблема исчезла. Это не так. Другой программист не думал, что его код был не столь важен, чтобы заслужить эти атрибуты. И, ну, это не потому, что всегда есть скрытая ошибка в коде, который использует код try / catch-em-all. Код покемона.

Так что Microsoft пришлось искать другой способ помочь программистам справиться с дерьмовым библиотечным кодом. Они сделали. Отметьте этот флажок, бац, решено. В любом случае, вы ничего не можете сделать с этим дерьмовым кодом, кроме как посылать гадости автору. Не позволяйте нам или Microsoft замедлить ваше выполнение этого процесса, вам придется смириться с тем, чтобы создать продукт, который любят использовать люди.

2 голосов
/ 08 марта 2011

Я думаю, что это невозможно в Visual Studio, но, конечно, в WinDbg. См. Например http://blogs.msdn.com/b/alejacma/archive/2009/08/24/managed-debugging-with-windbg-breaking-on-an-exception-part-1.aspx

С другой стороны, кажется, что начиная с Visual Studio 2010, вы можете загружать и использовать DLL-библиотеки расширений WinDbg, напрямую обеспечивающие дополнительную функциональность (включая, возможно, ту, которая вам нужна), но я еще не пробовал - см., Например, http://www.dotnetcurry.com/ShowArticle.aspx?ID=648

0 голосов
/ 08 декабря 2016

Что вы можете сделать, это использовать Concord , механизм отладки, который поставляется с Visual Studio (начиная с версии 2012). Он достаточно расширяемый через красивый управляемый API (и может быть развернут с использованием технологии vsix), но он не полностью документирован.

В Concord есть концепция отладочных мониторов, которую мы можем подключить, используя IDkmDebugMonitorExceptionNotification Interface

Самое классное, что этот интерфейс может отслеживать все исключения брошенные. Он также может «подавлять» любое обнаруженное исключительное событие, и это именно то, что нам нужно.

Я предлагаю начать с образца 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.

Примечание: как вы можете видеть, есть некоторая хитрость, чтобы гарантировать нормальное завершение программы.

...