Main | Overriding GetHashCode for value types »

08 September 2010

Fixing Debug > Exceptions settings programmatically

I have a love/hate relationship with the Visual Studio Debug > Exceptions dialog (as I have written previously). I love the functionality it provides (dropping into the debugger when an exception occurs) but I hate, well, pretty much everything else about it. (And, sadly, it appears to be completely unchanged in Visual Studio 2010.)

Visual Studio Exceptions dialog

Visual Studio persists in losing the settings I painstakingly set up (for our solution, I need to add two standard exception types that are mysteriously absent from its default list, and uncheck five exceptions that are thrown a lot by the framework but don't indicate any serious problem), which is very frustrating.

So I was very excited today to discover that these settings are automatable through the ExceptionSettings.aspx) interface exposed by the EnvDTE90 assembly (i.e., Visual Studio core automation). I immediately fired up the VS macro editor and wrote a VB macro that would iterate through all of the exceptions and turn on "Break when thrown" for each of them. I ran it... and waited... and waited... and gave up and terminated devenv.exe. It turns out that (running at 100% CPU on a Core i7), the COM interface can modify approximately one checkbox per second. It would probably take 10 minutes to check all the checkboxes in this dialog (during which time, of course, the VS UI is completely frozen).

Others have noticed this, but no one has found a solution. A StackOverflow answer comes close but still doesn't solve the problem.

I now use a hybrid solution: I open the dialog and check each top-level "Thrown" checkbox to check all its children. I close the dialog then run a macro which adds the missing exceptions and unchecks the handful of exceptions I don't want to break on. It still takes 15 seconds to run, but it's far better than having to do it by hand.

To use this code, open the Macro Explorer and right-click to add a new module (call it "ExceptionsModule"). Paste the following code in, then right-click the "FixExceptions" macro and choose Run to execute it.

Update: Added the GetOrCreateException function to deal with the situation where VS resets to factory defaults and removes previously-added exceptions.

    Imports EnvDTE90
    Imports System.Runtime.InteropServices
    Public Module ExceptionsModule
        Public Function GetGroup(ByVal name As String) As ExceptionSettings
            Dim dbg As Debugger3 = DTE.Debugger
            GetGroup = dbg.ExceptionGroups.Item(name)
        End Function

        ' Gets or creates the exception with the specified name in the specified group.
        Public Function GetOrCreateException(ByVal group As ExceptionSettings, ByVal name As String) As ExceptionSetting
            Try
                GetOrCreateException = group.Item(name)
            Catch ex As COMException When ex.ErrorCode = -2147352565 ' DISP_E_BADINDEX
                GetOrCreateException = group.NewException(name, 0)
            End Try
        End Function

        ' Gets or creates (with the specified name) the exception with the specified code in the specified group.
        Public Function GetOrCreateException(ByVal group As ExceptionSettings, ByVal name As String, ByVal code As UInteger) As ExceptionSetting
            Try
                GetOrCreateException = group.ItemFromCode(code)
            Catch ex As COMException When ex.ErrorCode = -2147352565 ' DISP_E_BADINDEX
                GetOrCreateException = group.NewException(name, code)
            End Try
        End Function

        Public Sub FixExceptions()
            Dim group As ExceptionSettings
            Dim setting As ExceptionSetting

            ' Common Language Runtime Exceptions
            group = GetGroup("Common Language Runtime Exceptions")
            For Each exceptionName In New String() { _
                    "System.FormatException", _
                    "System.NotSupportedException", _
                    "System.Configuration.ConfigurationErrorsException", _
                    "System.Deployment.Application.InvalidDeploymentException", _
                    "System.IO.FileNotFoundException", _
                    "System.Net.WebException"}
                setting = GetOrCreateException(group, exceptionName)
                group.SetBreakWhenThrown(False, setting)
            Next

            ' Managed Debugging Assistants
            group = GetGroup("Managed Debugging Assistants")
            setting = group.Item("BindingFailure")
            group.SetBreakWhenThrown(False, setting)

            ' Win32 Exceptions
            group = GetGroup("Win32 Exceptions")
            setting = GetOrCreateException(group, "RPC Canceled", 1818)
            group.SetBreakWhenThrown(False, setting)
        End Sub
    End Module

For more on scripting this dialog, see John Robbins' post, Customizing Exception Handler in the VS Debugger.

Posted by Bradley Grainger at September 08, 2010 09:55 AM