How to set "Break on All Exceptions", from a Package
.net, visual-studio, visual-studio-2010, visual-studio-debugging, visual-studio-extensions
Solution
The automation interfaces are out of the question. In an attempt to improve performance using them, I created a cache from exception group to `ExceptionSettings` object and exception name to the `ExceptionSetting` object. This allowed me to bypass `ExceptionSettings.Item` for rapid lookup of individual exceptions for calling `SetBreakWhenThrown`, but unfortunately the internal implementation of `SetBreakWhenThrown` includes a call to validate the arguments, which in turn triggers an internal enumeration process that plagues this entire approach. The cache is about 4 times faster than code not using a macro, but we're still talking about code that will hang the IDE for several minutes...
NOTE: The instructions below were only tested so far with Visual Studio 2012.
Stepping through `SetBreakWhenThrown` in disassembly view revealed that the critical internal call (after validation) is `sdm::CDebugManager::SetException`. It turns out that the shell debugger (`SVsShellDebugger` service which you cast to `IVsDebugger`) implements `IDebuggerInternal` which provides access to the current `IDebugSession3`. This property was non-null after I opened a solution but before I started debugging.
IDebuggerInternal debugger = Package.GetGlobalService(typeof(SVsShellDebugger)) as IDebuggerInternal;
IDebugSession3 session = debugger != null ? debugger.CurrentSession : null;
Note: The `IDebuggerInternal` interface is defined in:
Microsoft.VisualStudio.Debugger.Interop.Internal, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Using information returned by `EnumSetExceptions`, I created a structure that successfully alters the settings for a CLR exception! Call `IDebugSession3.SetException` to enable the debugger halting when the exception is thrown.
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);
To disable the debugger halting, use `IDebugSession3.RemoveSetException` instead.
Problem
I want to make an extension to quickly toggle breaking on CLR exceptions in debugger. I have made tried several approaches, neither of which is satisfactory. Here is what I have already tried: `ExceptionSettings.SetBreakWhenThrown` (MSDN) This is extremely slow (see this Connect issue). I have tried approaches from question "Toggle “Break when an exception is thrown.” using macro or keyboard shortcut" and neither seem to work reliably: in most cases only top level checkbox gets set, and it does not actually break on exceptions when debugging. Call `DTE.ExecuteCommand("Debug.Exceptions")` to show the window, and call `SetWindowsHookEx` (MSDN) just before that to intercept it before it appears (so that there is no flash to the user). This seems possible as I was able to intercept the message and get `HWND`. But it seems hacky and window is not that easy to manipulate properly (it has some weird combination of `SysListView32` with custom checkboxes and `SysTreeView32`). So I am leaving it as a last chance solution. Somehow get `IDebugEngine2` (MSDN) for managed code and call `IDebugEngine2.SetException` (MSDN) at the start of the debugging session. This seems possible, but I am having problems getting a debug engine. I have tried approach with `IVsLoader` described on MSDN forums, but I am pretty sure it gives me a new instance unrelated to the debugging session. I have also asked the question here: "Visual Studio: How to get IDebugEngine2 from VS Package (except IVsLoader)", but did not get a solution. I have tried using `IVsDebugger.AdviseDebugEventCallback` (MSDN) and passing in implementation of `IDebugEventCallback2` (MSDN), but I am always getting `null` for `pEngine` (and no `IDebugEngineCreateEvent2` either). I do get `IDebugSessionCreateEvent2` (undocumented?) and can get `IDebugSession2` from it, but its `SetException` call always gives me an `HRESULT` for wrong argument, so I might be missing something here (calling `SetException` on engine from `IVsLoader` gives OK, just does not work). Is there some other approach that is better than those or have I missed something in the existing ones? UPDATE/NOTE: If you found this question because you want a faster "Break on All Exceptions", I have made a free extension you can get from Visual Studio Gallery: Exception Breaker.