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.
Tell the debugger to break when an exception is thrownIn the Exception Settings window (Debug > Windows > Exception Settings), expand the node for a category of exceptions, such as Common Language Runtime Exceptions. Then select the check box for a specific exception within that category, such as System.
Exception breakpoints: suspend the program when Throwable or its subclasses are thrown. They apply globally to the exception condition and do not require a particular source code reference.
To set Visual Studio debugger options, select Tools > Options, and under Debugging select or deselect the boxes next to the General options. You can restore all default settings with Tools > Import and Export Settings > Reset all settings.
To turn off stop on exceptions press " Ctrl + Alt + E ". This will open the Exceptions window . Untick "Common Language Runtime Exceptions - Thrown". That would prevent it from pausing from within the delegate, but not when it's rethrown on Wait .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With