In the last two weeks I’ve gotten many questions about how to automate and program the exception handling the Visual Studio debugger so I thought I’d show how to some cool macros I’ve created to make it easy. If you’re interested in more about making the most of the debugger you can always come to our Devscovery conference in New York City starting on April 14th. (Bethany, Wintellect’s marketing maestro, is going to love me for working in the advertisement there!).
All the questions I got about the debugger exception handling fell into two buckets. The first was that teams have custom exceptions that they want to add to the Exceptions dialog and don’t want to have to add those manually every time the move to a new machine. Since the Exception settings are stored in the .SUO file next to the solution, you also lose those exceptions if you delete the .SUO file. The second question was about programmatically setting just a handful of exceptions to stop when thrown. What was interesting about the second question is that the people asking had the neat idea of having their tests running under the Visual Studio debugger and configured to ignore all exceptions but a handful. That way they’d have those tough error conditions already in the debugger in order to make their debugging easier. I thought that was a very interesting idea.
As with most things in life, there’s some good news and bad news about programmatically manipulating exceptions through the Visual Studio automation model. The good news is that the automation model has everything you need. The bad news is that certain operations, like setting all exceptions to stop when thrown, if done with a macro as so abysmally slow that it’s essentially unusable. I suspect the performance would be better if I wrote an add-in. I’m hoping the VS 2010 will improve the performance of macros so more people will consider writing quick extensions to the IDE.
Let me start by showing you a simple macro from Jim Griesmer that sets a CLR exception to stop when thrown:
Sub BreakWhenThrown(Optional ByVal strException As String = “”)
Dim dbg As Debugger3 = DTE.Debugger
Dim eg As ExceptionSettings = _
dbg.ExceptionGroups.Item(“Common Language Runtime Exceptions”)
eg.SetBreakWhenThrown(True, eg.Item(strException))
End Sub
To execute the above macro, you’ll open the Command window and pass the full name of the exception on the command line like the following:
>Macros.MyMacros.CLRExceptions.BreakWhenThrown System.ArgumentException
The macro itself if relatively straightforward. Once you have the Debugger3 interface, you can get the exceptions under a category by name. As you probably guessed the names of the exception groups maps to exactly what you see in the Exceptions dialog in Visual Studio.
Note that my Exception dialog probably looks different than yours because I turned off Just My Code in the Visual Studio Options dialog (Options, Debugging, General). I highly recommend you do as well because having Just My Code turned on turns off very valuable features such as debugging optimized builds and the awesome .NET Reference Source Code debugging.
The real work in the macro is all in the ExceptionsGroup interface as it has the methods on it to set, create, and remove individual exceptions. To get an individual exception, you access the ExceptionsGroup Items collection by exception name to get the ExceptionSetting.
At the bottom of this blog entry, I included the macro source code for a set of macros that wrap up adding, removing, and configuring CLR exceptions easy with full error handling and reporting. For those of you doing native development, you’ll get the idea what you need to do. The one difference with Win32 Exceptions verses the other categories of exceptions is that you’ll need to specify the exception codes to those exceptions.
In the code I wanted to include macros that would let me set or clear stopping when exceptions were thrown. The problem was that the macro literally took more than 15 minutes to enumerate and set each exception setting. It’s faster to manually bring up the Exceptions dialog and click the check box next to the category. I’ll keep looking to see if I can find a faster way to enable and disable stopping when thrown.
Those of you using my Debugger Settings add in I’m working on adding support for exception settings to it. Keep reading this space for updates. I’m worried about the performance of saving and restoring the exception settings given the horrible performance I’m seeing from the macro so it may turn out I won’t add it.
The team that was running their tests under the debugger and wanted to automate setting various exceptions also was looking for a way to programmatically execute a macro in Visual Studio. They wanted to be able to configure their special exceptions as well as automatically attach or start their application. Fortunately, that’s easy to do with the /command command line option to DEVENV.EXE. That will start the IDE and execute a Visual Studio command or macro.
As always, let me know if you have any questions or have an idea you’d like to see explored.
”””””””””””””””””””””””””””””””””””””””’
‘ CLRExceptions – John Robbins (c) 2009 – john@training.atmosera.com
‘ Macros that make it easier to manipulate CLR exceptions in the debugger.
‘
‘ Do whatever you want with this code.
‘
‘ Version 1.0 – April 1, 2009
‘ – Initial version.
”””””””””””””””””””””””””””””””””””””””’
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Text
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
Public Module CLRExceptions
‘ Adds a new exception name to the list of CLR exceptions.
Sub CLRExceptionCreate(Optional ByVal strException As String = “”)
CreateDeleteCLRException(strException, True)
End Sub
‘ Removes an exception from the list of CLR exceptions.
Sub CLRExceptionDelete(Optional ByVal strException As String = “”)
CreateDeleteCLRException(strException, False)
End Sub
‘ Sets the specifically named exception to stop when thrown.
Sub CLRExceptionBreakWhenThrown(Optional ByVal strException As String = “”)
SetClrException(strException, True)
End Sub
‘ Sets the specifically named exception to continue when thrown.
Sub CLRExceptionContinueWhenThrown(Optional ByVal strException _
As String = “”)
SetClrException(strException, False)
End Sub
‘ Helper method to create or delete a CLR exception.
Private Sub CreateDeleteCLRException(ByVal strException As String, _
ByVal create As Boolean)
Dim dbg As Debugger3 = DTE.Debugger
Dim cmdWindow As CmdWindow = New CmdWindow()
If (True = String.IsNullOrEmpty(strException)) Then
cmdWindow.WriteLine(“You must pass the exception as the parameter”)
Return
End If
‘ If ExceptionGroups is null, there’s no project loaded.
If (dbg.ExceptionGroups IsNot Nothing) Then
Dim eg As ExceptionSettings = _
dbg.ExceptionGroups.Item(“Common Language Runtime Exceptions”)
Try
If (True = create) Then
eg.NewException(strException, 100)
cmdWindow.WriteLine(“New CLR exception created: ” + _
strException)
Else
eg.Remove(strException)
cmdWindow.WriteLine(“CLR exception deleted: ” + _
strException)
End If
Catch ex As COMException
cmdWindow.WriteLine(ex.Message)
End Try
Else
cmdWindow.WriteLine(“You must open a solution to set exceptions”)
End If
End Sub
‘ Helper method to have a CLR exception stop or continue when thrown.
Private Sub SetClrException(ByVal strException As String, _
ByVal enabled As Boolean)
Dim dbg As Debugger3 = DTE.Debugger
Dim cmdWindow As CmdWindow = New CmdWindow()
If (True = String.IsNullOrEmpty(strException)) Then
cmdWindow.WriteLine(“Missing exception parameter”)
Return
End If
Dim eg As ExceptionSettings = _
dbg.ExceptionGroups.Item(“Common Language Runtime Exceptions”)
‘ If ExceptionGroups is null, there’s no project loaded.
If (dbg.ExceptionGroups IsNot Nothing) Then
Try
eg.SetBreakWhenThrown(enabled, eg.Item(strException))
Dim msg As String = “continue”
If (True = enabled) Then
msg = “break”
End If
cmdWindow.WriteLine(“Exception ‘” + strException + _
“‘ will ” + msg + ” when thrown”)
Catch ex As COMException
cmdWindow.WriteLine(“Problem setting exception: ” + ex.Message)
End Try
Else
cmdWindow.WriteLine(“You must open a solution to set exceptions”)
End If
End Sub
EndModule
‘ Drop this into a module called Utilites
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Globalization
Imports System.Text.RegularExpressions
Imports System.Collections.Generic
Public Module Utilities
‘ Stuff a new GUID into the current cursor location.
Sub InsertGuid()
Dim objTextSelection As TextSelection
objTextSelection = CType(DTE.ActiveDocument.Selection(), _
EnvDTE.TextSelection)
Dim cult As CultureInfo = _
System.Threading.Thread.CurrentThread.CurrentUICulture
objTextSelection.Text = Guid.NewGuid.ToString.ToUpper(cult)
End Sub
‘ My wrapper around the CommandWindow to make it easier to use.
Public Class CmdWindow
‘ The internal Command window.
Private m_CmdWnd As CommandWindow
‘ The constructor that simply gets the command window.
Public Sub New()
m_CmdWnd = CType(DTE.Windows.Item(EnvDTE.Constants.vsWindowKindCommandWindow).Object, CommandWindow)
End Sub
‘ Public method to write a line with a CRLF appended.
Public Sub WriteLine(Optional ByVal Str As String = “”)
m_CmdWnd.OutputString(Str + vbCrLf)
End Sub
‘ Public method to write a line.
Public Sub Write(ByVal Str As String)
m_CmdWnd.OutputString(Str)
End Sub
‘ Clears the command window.
Public Sub Clear()
m_CmdWnd.Clear()
End Sub
‘ Sends the input to the command window like you typed it.
Public Sub SendInput(ByVal Command As String, Optional ByVal Commit As Boolean = True)
m_CmdWnd.SendInput(Command, Commit)
End Sub
End Class
Public Class OutputPane
‘ The output pane this class wraps.
Private m_OutPane As OutputWindowPane
‘ The class constructor.
Public Sub New(ByVal Name As String, _
Optional ByVal ShowIt As Boolean = True)
‘ First, get the main output window itself.
Dim Win As Window = _
DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput)
‘ Show the window if I’m supposed to.
If (True = ShowIt) Then
Win.Visible = True
End If
‘ Convert the window to a real output window.
‘ The VB way of casting!
Dim OutWin As OutputWindow = CType(Win.Object, OutputWindow)
‘ Use exception handling to access this pane if it already exists.
Try
m_OutPane = OutWin.OutputWindowPanes.Item(Name)
Catch e As System.Exception
‘ This output tab doesn’t exist, so create it.
m_OutPane = OutWin.OutputWindowPanes.Add(Name)
End Try
‘ If it’s supposed to be visible, make it so.
If (True = ShowIt) Then
m_OutPane.Activate()
End If
End Sub
‘ Allows access to the value itself.
Public ReadOnly Property OutPane() As OutputWindowPane
Get
Return m_OutPane
End Get
End Property
‘ Wrapper. Get the underlying object for all the rest
‘ of the OutputWindowPane methods.
Public Sub Clear()
m_OutPane.Clear()
End Sub
‘ The functions I wanted to add.
Public Sub WriteLine(ByVal Text As String)
m_OutPane.OutputString(Text + vbCrLf)
End Sub
Public Sub Write(ByVal Text As String)
m_OutPane.OutputString(Text)
End Sub
End Class
‘ SplitParameters – Splits up a parameter string passed to a macro
‘ into an array of parameters.
Function SplitParameters(ByVal parameterString As String) As String()
‘ No sense doing anything if no parameters.
If (0 = parameterString.Length) Then
Exit Function
End If
‘ Cool regex from Michal Ash:
‘ http://regexlib.com/REDetails.aspx?regexp_id=621
Dim initialArray As String()
initialArray = Regex.Split(parameterString, _
“,(?!(?<=(?:^|,)s*””(?:[^””]|””””|””)*,)” & _
“(?:[^””]|””””|””)*””s*(?:,|$))”, _
RegexOptions.Singleline)
‘ Go through and scrape off any extra whitespace on parameters.
Dim returnArray As List(Of String) = New List(Of String)
Dim i As Int32
For i = 0 To initialArray.Length – 1
returnArray.Add(initialArray(i).Trim())
Next
Return (returnArray.ToArray())
End Function
End Module
Copyright © 1995 – 2016, Atmosera, Inc. All rights reserved.
The following legal and privacy policy cover Atmosera, Inc. and its doing business as (dba) EasyStreet Online Services.
Atmosera is a registered trademark of Atmosera, Inc. Certain names, logos designs, titles, words or phrases on this site may constitute trademarks, servicemarks or tradenames of Atmosera or other entities, which may be registered in certain jurisdictions.
Privacy Policy
- Atmosera is committed to respecting your privacy.
- Once you choose to provide personally identifiable information, it will only be used in the context of your relationship with Atmosera.
- Atmosera will not give, sell, rent or lease your personally identifiable information to others unless required by law or your prior permission is obtained.
Acceptable Use Policy
This Acceptable Use Policy (the “AUP”) applies to the use of the services of Atmosera and its affiliated companies (“Atmosera”) by its customers (the “Services”). This AUP supplements, but does not supersede, the agreements that customers or other users (“You”) have with Atmosera. The AUP also applies, and should be communicated, to each Atmosera customer’s own customers and/or end users that utilize the Services provided through the Atmosera customer. In the event of any conflict between a customer’s agreement and the AUP, the AUP will govern. By using the Services, you agree to the latest version of this AUP. Please read it carefully.
Atmosera has published this AUP to help ensure that the Services provided to our customers are of the highest quality, and to help protect the privacy and security of our customers, systems, and networks, while also encouraging responsible use, including compliance with applicable laws. Additionally, the AUP describes types of use of the Services that are prohibited. Atmosera may at its sole discretion determine whether a use of the Services is a violation of the AUP. Atmosera in no way intends to monitor, control, or censor communications or content that a customer may acquire, transmit, or store on or via Atmosera’s network. However, when we have knowledge of a violation of this AUP, we reserve the right to take such action as is necessary to address the violation, as referenced below.
Definitions
As used herein, “Content” shall mean all information made available, displayed, transmitted or retransmitted in connection with customer’s use of the Services (including, without limitation, information made available by means of a “hot link”, a third party posting or similar means) including all trademarks, service marks and domain names contained therein as well as the contents of any bulletin boards, social sites or chat forums, and, all updates, upgrades, modifications and other versions of any of the foregoing.
Illegal/Criminal Activity
The Services may not be used in connection with any criminal or civil violations of local, state, provincial, federal, or international law, treaty, regulations, court order, ordinance, administrative rule, or other government requirements.
Intellectual Property
The Services may not be used to transmit, re-transmit, host or store any Content or to engage in any activity that infringes or facilitates the infringement of the intellectual property rights or privacy rights of any individual, group or entity, including but not limited to any rights protected by any copyright, patent, trademark, trade secret, trade dress, right of privacy, right of publicity, moral rights or other intellectual property right now known or later recognized by statute, judicial decision or regulation.
Threats
The Services may not be used to transmit, re-transmit, host or store materials or Content that is of a threatening nature, including threats of death or physical harm, or that is harassing, libelous, and or defamatory, invasive of privacy rights, or to provide information or assistance in causing or carrying out violence against any government, organization, group or individual.
Spam
Spam is an unacceptable use of the Services and is a violation of the AUP. Prohibited acts include, but are not limited to, any of the following activities:
- Posting a single message or messages similar in content, to more than five online forums or newsgroups.
- Posting of messages to online forums or newsgroups that violate the rules of the forums or newsgroups.
- Collecting the responses from unsolicited email.
- Sending any unsolicited email that could be expected, in Atmosera’s sole discretion, to provoke complaints.
Indirect Access
A violation of this AUP by someone having only indirect access to the Services through a Customer or other user will be considered a violation by the Customer or other user, whether or not with the knowledge or consent of the Customer or other user. In addition, this AUP applies to any email or Content transmitted by a customer or on its behalf using a Atmosera account as a mailbox for responses or promoting Content hosted or transmitted using the Services, or indicating in any way that Atmosera was involved in the transmission of such email or Content.