Подтвердить что ты не робот

Как установить "Break on All Exceptions", из пакета

Я хочу сделать расширение для быстрого переключения на исключения CLR в отладчике.
Я сделал несколько попыток, ни один из которых не является удовлетворительным.

Вот что я уже пробовал:

  • ExceptionSettings.SetBreakWhenThrown (MSDN)
    Это очень медленно (см. эта проблема подключения). Я пробовал подходы к вопросу "Toggle "Перерыв при вызове исключения" с использованием макроса или сочетания клавиш" и, похоже, не работают надежно: в большинстве случаев только верхний уровень checkbox устанавливается, и при отладке он фактически не прерывает исключения.

  • Вызовите DTE.ExecuteCommand("Debug.Exceptions"), чтобы отобразить окно, и вызовите SetWindowsHookEx (MSDN) перед этим для перехвата перед тем, как он появится (чтобы не было вспышки для пользователя). Это кажется возможным, поскольку я смог перехватить сообщение и получить HWND. Но кажется, что взломать и окно не так легко манипулировать должным образом (у него есть какая-то странная комбинация SysListView32 с пользовательскими флажками и SysTreeView32). Поэтому я оставляю это как последнее решение.

  • Как-то получить IDebugEngine2 (MSDN) для управляемого кода и вызвать IDebugEngine2.SetException (MSDN) в начале сеанса отладки. Это кажется возможным, но у меня возникают проблемы с отладчиком. Я пробовал использовать IVsLoader на форумах MSDN, но я уверен, что он дает мне новый экземпляр, не имеющий отношения к сеансу отладки.

    Я также задал здесь вопрос: "Visual Studio: как получить IDebugEngine2 из пакета VS (кроме IVsLoader)", но не получил решения.

    Я попытался использовать IVsDebugger.AdviseDebugEventCallback (MSDN) и передать в реализацию IDebugEventCallback2 (MSDN), но я всегда получаю null для pEngine (и no IDebugEngineCreateEvent2).

    Я получаю IDebugSessionCreateEvent2 (недокументированный?) и могу получить IDebugSession2 от него, но его вызов SetException всегда дает мне HRESULT для неправильного аргумента, поэтому я мог бы здесь что-то пропустить (вызов SetException на двигателе от IVsLoader дает ОК, просто не работает).

Есть ли другой подход, который лучше, чем те, или я что-то пропустил в существующих?


UPDATE/Примечание:
Если вы нашли этот вопрос, потому что вам нужен более быстрый "Break on All Exceptions", я сделал бесплатное расширение, которое вы можете получить в галерее Visual Studio: Exception Breaker.

4b9b3361

Ответ 1

Интерфейсы автоматизации не могут быть и речи. В целях повышения производительности с их помощью я создал кеш из группы исключений в объект ExceptionSettings и имя исключения для объекта ExceptionSetting. Это позволило мне обойти ExceptionSettings.Item для быстрого поиска отдельных исключений для вызова SetBreakWhenThrown, но, к сожалению, внутренняя реализация SetBreakWhenThrown включает в себя вызов для проверки аргументов, что, в свою очередь, вызывает внутренний процесс перечисления, который наносит весь этот подход, Кэш примерно в 4 раза быстрее, чем код, не использующий макрос, но мы все еще говорим о коде, который будет висеть IDE в течение нескольких минут...

ПРИМЕЧАНИЕ. Инструкции ниже были проверены до сих пор с помощью Visual Studio 2012.

Пройдя через SetBreakWhenThrown в режиме разборки, выяснилось, что критический внутренний вызов (после проверки) составляет sdm::CDebugManager::SetException. Оказывается, что отладчик оболочки (SVsShellDebugger, который вы используете для IVsDebugger) реализует IDebuggerInternal, который обеспечивает доступ к текущему IDebugSession3. Это свойство было не равным null после того, как я открыл решение, но до начала отладки.

IDebuggerInternal debugger = Package.GetGlobalService(typeof(SVsShellDebugger)) as IDebuggerInternal;
IDebugSession3 session = debugger != null ? debugger.CurrentSession : null;

Примечание. Интерфейс IDebuggerInternal определяется в:

Microsoft.VisualStudio.Debugger.Interop.Internal, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

Используя информацию, возвращенную EnumSetExceptions, я создал структуру, которая успешно изменяет настройки для исключения CLR! Вызовите IDebugSession3.SetException, чтобы отключить отладчик при вызове исключения.

EXCEPTION_INFO[] exceptionInfo =
{
    new EXCEPTION_INFO()
    {
        bstrExceptionName = typeof(NullReferenceException).FullName,
        bstrProgramName = null,
        dwCode = 0,
        pProgram = null,
        guidType = VSConstants.DebugEnginesGuids.ManagedOnly_guid,
        dwState = enum_EXCEPTION_STATE.EXCEPTION_STOP_FIRST_CHANCE
            | enum_EXCEPTION_STATE.EXCEPTION_STOP_SECOND_CHANCE
            | enum_EXCEPTION_STATE.EXCEPTION_JUST_MY_CODE_SUPPORTED
            | enum_EXCEPTION_STATE.EXCEPTION_STOP_USER_FIRST_CHANCE
            | enum_EXCEPTION_STATE.EXCEPTION_STOP_USER_UNCAUGHT
    }
};
hr = session.SetException(exceptionInfo);

Чтобы отключить останов отладчика, используйте IDebugSession3.RemoveSetException вместо этого.