Carlos Quintero (Microsoft MVP) blog

Visual Studio Extensibility with Add-ins

March 2008 - Posts

I have updated a quite old but useful article (Feb 2005) to document the new /ResetAddin command-line flag of Visual Studio 2005/2008 and how to do it correctly:

HOWTO: Removing commands and UI elements during Visual Studio .NET add-in uninstallation
http://www.mztools.com/articles/2005/MZ2005002.aspx

It is important to note that the /ResetAddin command-line flag will remove permanent controls but not permanent commandbars (another reason to not use them). 

Posted by carlosq | with no comments

Just a curious difference in behavior between Visual Studio 2005 and Visual Studio 2008 that the MSDN documentation doesn't cover very well:

According to the MSDN documentation of VS 2005, "This switch prevents all third-party VSPackages from loading when Visual Studio starts, thus ensuring stable execution." One would expect that if third-party VSPackages are not loaded, add-ins would be also excluded from loading, but they aren't, they are loaded.

Visual Studio 2008 works as expected and the whole Add-in Manager is disabled.

Posted by carlosq | 2 comment(s)

Some days ago I posted a simple RichTextBox-based source code colorizer, but after testing with big code snippets it happened that it didn't perform very well (it took several seconds), so I rewrote it from scratch. This time I use a low level approach and I compose the RTF text directly. The result algorithm performs very well (less than a second) for big code snippets and it works with the RichTextBox of .NET Framework which didn't support the SelectionBackColor property. Here is the code:

Public Class FormCodeColorizer

   Private Sub ButtonColorize_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonColorize.Click

      Dim objDTE As EnvDTE.DTE
      Dim objType As Type


      objType = System.Type.GetTypeFromProgID("VisualStudio.DTE")
      objDTE = CType(System.Activator.CreateInstance(objType), EnvDTE.DTE)

      ColorizeRichText(objDTE, Me.RichTextBoxCode, "'", GetVBKeywords())

      objDTE.Quit()

   End Sub

   Public Sub ColorizeRichText(ByVal objDTE As EnvDTE.DTE, ByVal ctlRichTextBox As RichTextBox, ByVal sCommentLinePrefix As String, ByVal colKeywords As Collections.Specialized.StringCollection)

      Const DISPLAY_ITEM_PLAIN_TEXT As String = "Plain Text"
      Const DISPLAY_ITEM_KEYWORD As String = "Keyword"
      Const DISPLAY_ITEM_STRING As String = "String"

#If VS_8_0 Then
      ' PATCH: The Comment default color is wrong. We use the CSS Comment color instead
      Const DISPLAY_ITEM_COMMENT As String = "CSS Comment"
#Else
      Const DISPLAY_ITEM_COMMENT As String = "Comment"
#End If

      Dim iSelectionStart As Integer
      Dim iSelectionLength As Integer

      Dim colFontsAndColorsItems As EnvDTE.FontsAndColorsItems = Nothing
      Dim sFontFamily As String = ""
      Dim sngFontSize As Single = 0.0

      Dim objRtfHeaderStringBuilder As New System.Text.StringBuilder()
      Dim objRtfBodyStringBuilder As New System.Text.StringBuilder()
      Dim sLine As String
      Dim sHeaderRtf As String
      Dim sBodyRtf As String

      Dim sPlainTextBackColorIndex As String = "1"
      Dim sPlainTextForeColorIndex As String = "2"
      Dim sKeywordBackColorIndex As String = "3"
      Dim sKeywordForeColorIndex As String = "4"
      Dim sCommentBackColorIndex As String = "5"
      Dim sCommentForeColorIndex As String = "6"
      Dim sStringBackColorIndex As String = "7"
      Dim sStringForeColorIndex As String = "8"

      Dim objPlainTextBackColor As Color
      Dim objPlainTextForeColor As Color
      Dim objKeywordBackColor As Color
      Dim objKeywordForeColor As Color
      Dim objCommentBackColor As Color
      Dim objCommentForeColor As Color
      Dim objStringBackColor As Color
      Dim objStringForeColor As Color

      Dim bPlainTextBold As Boolean
      Dim bKeywordBold As Boolean
      Dim bCommentBold As Boolean
      Dim bStringBold As Boolean

      Dim sKeyword As String

      Dim sRtfKeywordBefore As String = ""
      Dim sRtfKeywordAfter As String = ""
      Dim sRtfBefore As String = ""
      Dim sRtfAfter As String = ""
      Dim sRtf As String

      If GetTextEditorFontAndColorsItems(objDTE, sFontFamily, sngFontSize, colFontsAndColorsItems) Then

         Try

            ' Cache selection position and length
            iSelectionStart = ctlRichTextBox.SelectionStart
            iSelectionLength = ctlRichTextBox.SelectionLength

            GetColorsFromFontsAndColorsItems(colFontsAndColorsItems, DISPLAY_ITEM_PLAIN_TEXT, objPlainTextBackColor, objPlainTextForeColor, bPlainTextBold)
            ctlRichTextBox.BackColor = objPlainTextBackColor

            objRtfHeaderStringBuilder.Append("\rtf1")   ' RTF Version 1.x
            objRtfHeaderStringBuilder.Append("\ansi")   ' ANSI
            objRtfHeaderStringBuilder.Append("\deff0")  ' Use font 0 as default

            objRtfHeaderStringBuilder.Append("{\fonttbl")  ' Font table group
            objRtfHeaderStringBuilder.Append("{\f0")     ' Font 0
            objRtfHeaderStringBuilder.Append("\fnil")     ' Use default font if specified font is not installed on the system
            objRtfHeaderStringBuilder.Append("\fcharset0 ")     ' Charset 0
            objRtfHeaderStringBuilder.Append(sFontFamily)  ' Font family name

            objRtfHeaderStringBuilder.Append(";}") ' End font 0 definition
            objRtfHeaderStringBuilder.Append("}")  ' End font table group

            objRtfHeaderStringBuilder.Append("{\colortbl ;")  ' Color table group

            ' Add colors to table
            AddColorsToRtfHeader(objRtfHeaderStringBuilder, colFontsAndColorsItems, DISPLAY_ITEM_PLAIN_TEXT, objPlainTextBackColor, objPlainTextForeColor, bPlainTextBold)
            AddColorsToRtfHeader(objRtfHeaderStringBuilder, colFontsAndColorsItems, DISPLAY_ITEM_KEYWORD, objKeywordBackColor, objKeywordForeColor, bKeywordBold)
            AddColorsToRtfHeader(objRtfHeaderStringBuilder, colFontsAndColorsItems, DISPLAY_ITEM_COMMENT, objCommentBackColor, objCommentForeColor, bCommentBold)
            AddColorsToRtfHeader(objRtfHeaderStringBuilder, colFontsAndColorsItems, DISPLAY_ITEM_STRING, objStringBackColor, objStringForeColor, bStringBold)

            objRtfHeaderStringBuilder.Append("}")  ' ' End Color table group

            objRtfHeaderStringBuilder.Append("\viewkind4")  ' View Kind: Normal
            objRtfHeaderStringBuilder.Append("\uc1")  ' Unicode information
            objRtfHeaderStringBuilder.Append("\pard")  '

            objRtfHeaderStringBuilder.Append("\tx270\tx540\tx810\tx1080\tx1350\tx1620\tx1890\tx2160\tx2430\tx2700")  ' Tabulations

            objRtfBodyStringBuilder.Append("\f0 ") ' Font 0
            objRtfBodyStringBuilder.Append("\fs" & CType(sngFontSize * 2, Integer).ToString & " ") ' Font size in half points
            If bPlainTextBold Then
               objRtfBodyStringBuilder.Append("\b ")
            End If

            If ctlRichTextBox.Text = "" Then
               objRtfBodyStringBuilder.Append("\par")
               sBodyRtf = objRtfBodyStringBuilder.ToString
            Else

               For Each sLine In ctlRichTextBox.Lines

                  sLine = EscapeSpecialRTFCharacters(sLine)

                  objRtfBodyStringBuilder.Append(sLine)  '
                  objRtfBodyStringBuilder.Append("\par")  '
                  objRtfBodyStringBuilder.Append(ControlChars.Cr)

               Next

               sBodyRtf = objRtfBodyStringBuilder.ToString

               ' Colorize keywords
               GetRtfBeforeAndAfter(objPlainTextBackColor, objPlainTextForeColor, bPlainTextBold, sPlainTextBackColorIndex, sPlainTextForeColorIndex, _
                           objKeywordBackColor, objKeywordForeColor, bKeywordBold, sKeywordBackColorIndex, sKeywordForeColorIndex, sRtfKeywordBefore, sRtfKeywordAfter)
               If sRtfKeywordBefore <> "" Then
                  For Each sKeyword In colKeywords
                     ' The \b regular expression means word boundary
                     sBodyRtf = System.Text.RegularExpressions.Regex.Replace(sBodyRtf, "\b" & sKeyword & "\b", sRtfKeywordBefore & sKeyword & sRtfKeywordAfter)
                  Next
               End If

               ' Colorize strings
               GetRtfBeforeAndAfter(objPlainTextBackColor, objPlainTextForeColor, bPlainTextBold, sPlainTextBackColorIndex, sPlainTextForeColorIndex, _
                  objStringBackColor, objStringForeColor, bStringBold, sStringBackColorIndex, sStringForeColorIndex, sRtfBefore, sRtfAfter)
               If sRtfBefore <> "" Then
                  sBodyRtf = ColorizeTokenBetweenDelimiters(sBodyRtf, ControlChars.Quote, ControlChars.Quote, sRtfBefore, sRtfAfter, sRtfKeywordBefore, sRtfKeywordAfter)
               End If

               ' Colorize comments
               If sCommentLinePrefix <> "" Then
                  GetRtfBeforeAndAfter(objPlainTextBackColor, objPlainTextForeColor, bPlainTextBold, sPlainTextBackColorIndex, sPlainTextForeColorIndex, _
                              objCommentBackColor, objCommentForeColor, bCommentBold, sCommentBackColorIndex, sCommentForeColorIndex, sRtfBefore, sRtfAfter)
                  If sRtfBefore <> "" Then
                     sBodyRtf = ColorizeTokenBetweenDelimiters(sBodyRtf, sCommentLinePrefix, ControlChars.Cr, sRtfBefore, sRtfAfter, sRtfKeywordBefore, sRtfKeywordAfter)
                  End If
               End If

            End If

            sHeaderRtf = objRtfHeaderStringBuilder.ToString

            sRtf = "{" & sHeaderRtf & sBodyRtf & "}"

            ctlRichTextBox.Rtf = sRtf

            ' Restore selection position and length
            ctlRichTextBox.SelectionStart = iSelectionStart
            ctlRichTextBox.SelectionLength = iSelectionLength

         Catch objException As Exception

            MessageBox.Show(objException.ToString)

         End Try

      End If

   End Sub

   Public Function GetVBKeywords() As System.Collections.Specialized.StringCollection

      Dim colVBKeywords As New System.Collections.Specialized.StringCollection

      colVBKeywords.Add("AddHandler")
      colVBKeywords.Add("AddressOf")
      colVBKeywords.Add("Alias")
      colVBKeywords.Add("And")
      colVBKeywords.Add("AndAlso")
      colVBKeywords.Add("As")
      colVBKeywords.Add("Boolean")
      colVBKeywords.Add("ByRef")
      colVBKeywords.Add("Byte")
      colVBKeywords.Add("ByVal")
      colVBKeywords.Add("Call")
      colVBKeywords.Add("Case")
      colVBKeywords.Add("Catch")
      colVBKeywords.Add("CBool")
      colVBKeywords.Add("CByte")
      colVBKeywords.Add("CChar")
      colVBKeywords.Add("CDate")
      colVBKeywords.Add("CDec")
      colVBKeywords.Add("CDbl")
      colVBKeywords.Add("Char")
      colVBKeywords.Add("CInt")
      colVBKeywords.Add("Class")
      colVBKeywords.Add("CLng")
      colVBKeywords.Add("CObj")
      colVBKeywords.Add("Const")
      colVBKeywords.Add("Continue")
      colVBKeywords.Add("CSByte")
      colVBKeywords.Add("CShort")
      colVBKeywords.Add("CSng")
      colVBKeywords.Add("CStr")
      colVBKeywords.Add("CType")
      colVBKeywords.Add("CUInt")
      colVBKeywords.Add("CULng")
      colVBKeywords.Add("CUShort")
      colVBKeywords.Add("Date")
      colVBKeywords.Add("Decimal")
      colVBKeywords.Add("Declare")
      colVBKeywords.Add("Default")
      colVBKeywords.Add("Delegate")
      colVBKeywords.Add("Dim")
      colVBKeywords.Add("DirectCast")
      colVBKeywords.Add("Do")
      colVBKeywords.Add("Double")
      colVBKeywords.Add("Each")
      colVBKeywords.Add("Else")
      colVBKeywords.Add("ElseIf")
      colVBKeywords.Add("End")
      colVBKeywords.Add("EndIf")
      colVBKeywords.Add("Enum")
      colVBKeywords.Add("Erase")
      colVBKeywords.Add("Error")
      colVBKeywords.Add("Event")
      colVBKeywords.Add("Exit")
      colVBKeywords.Add("False")
      colVBKeywords.Add("Finally")
      colVBKeywords.Add("For")
      colVBKeywords.Add("Friend")
      colVBKeywords.Add("Function")
      colVBKeywords.Add("Get")
      colVBKeywords.Add("GetType")
      colVBKeywords.Add("GetXMLNamespace")
      colVBKeywords.Add("Global")
      colVBKeywords.Add("GoSub")
      colVBKeywords.Add("GoTo")
      colVBKeywords.Add("Handles")
      colVBKeywords.Add("If")
      colVBKeywords.Add("Implements")
      colVBKeywords.Add("Imports")
      colVBKeywords.Add("In")
      colVBKeywords.Add("Inherits")
      colVBKeywords.Add("Integer")
      colVBKeywords.Add("Interface")
      colVBKeywords.Add("Is")
      colVBKeywords.Add("IsNot")
      colVBKeywords.Add("Let")
      colVBKeywords.Add("Lib")
      colVBKeywords.Add("Like")
      colVBKeywords.Add("Long")
      colVBKeywords.Add("Loop")
      colVBKeywords.Add("Me")
      colVBKeywords.Add("Mod")
      colVBKeywords.Add("Module")
      colVBKeywords.Add("MustInherit")
      colVBKeywords.Add("MustOverride")
      colVBKeywords.Add("MyBase")
      colVBKeywords.Add("MyClass")
      colVBKeywords.Add("Namespace")
      colVBKeywords.Add("Narrowing")
      colVBKeywords.Add("New")
      colVBKeywords.Add("Next")
      colVBKeywords.Add("Not")
      colVBKeywords.Add("Nothing")
      colVBKeywords.Add("NotInheritable")
      colVBKeywords.Add("NotOverridable")
      colVBKeywords.Add("Object")
      colVBKeywords.Add("Of")
      colVBKeywords.Add("On")
      colVBKeywords.Add("Operator")
      colVBKeywords.Add("Option")
      colVBKeywords.Add("Optional")
      colVBKeywords.Add("Or")
      colVBKeywords.Add("OrElse")
      colVBKeywords.Add("Overloads")
      colVBKeywords.Add("Overridable")
      colVBKeywords.Add("Overrides")
      colVBKeywords.Add("ParamArray")
      colVBKeywords.Add("Partial")
      colVBKeywords.Add("Private")
      colVBKeywords.Add("Property")
      colVBKeywords.Add("Protected")
      colVBKeywords.Add("Public")
      colVBKeywords.Add("RaiseEvent")
      colVBKeywords.Add("ReadOnly")
      colVBKeywords.Add("ReDim")
      colVBKeywords.Add("REM")
      colVBKeywords.Add("RemoveHandler")
      colVBKeywords.Add("Resume")
      colVBKeywords.Add("Return")
      colVBKeywords.Add("SByte")
      colVBKeywords.Add("Select")
      colVBKeywords.Add("Set")
      colVBKeywords.Add("Shadows")
      colVBKeywords.Add("Shared")
      colVBKeywords.Add("Short")
      colVBKeywords.Add("Single")
      colVBKeywords.Add("Static")
      colVBKeywords.Add("Step")
      colVBKeywords.Add("Stop")
      colVBKeywords.Add("String")
      colVBKeywords.Add("Structure")
      colVBKeywords.Add("Sub")
      colVBKeywords.Add("SyncLock")
      colVBKeywords.Add("Then")
      colVBKeywords.Add("Throw")
      colVBKeywords.Add("To")
      colVBKeywords.Add("True")
      colVBKeywords.Add("Try")
      colVBKeywords.Add("TryCast")
      colVBKeywords.Add("TypeOf")
      colVBKeywords.Add("Variant")
      colVBKeywords.Add("Wend")
      colVBKeywords.Add("UInteger")
      colVBKeywords.Add("ULong")
      colVBKeywords.Add("UShort")
      colVBKeywords.Add("Using")
      colVBKeywords.Add("When")
      colVBKeywords.Add("While")
      colVBKeywords.Add("Widening")
      colVBKeywords.Add("With")
      colVBKeywords.Add("WithEvents")
      colVBKeywords.Add("WriteOnly")
      colVBKeywords.Add("Xor")
      colVBKeywords.Add("#Const")
      colVBKeywords.Add("#Else")
      colVBKeywords.Add("#ElseIf")
      colVBKeywords.Add("#End")
      colVBKeywords.Add("#If")

      Return colVBKeywords

   End Function

   Public Function GetTextEditorFontAndColorsItems(ByVal objDTE As EnvDTE.DTE, ByRef r_sFontFamily As String, ByRef r_sngFontSize As Single, ByRef r_colFontsAndColorsItems As EnvDTE.FontsAndColorsItems) As Boolean

      Const CATEGORY_FONTS_AND_COLORS As String = "FontsAndColors"
      Const PAGE_TEXT_EDITOR As String = "TextEditor"

      Const PROPERTY_FONT_SIZE As String = "FontSize"
      Const PROPERTY_FONT_FAMILY As String = "FontFamily"
      Const PROPERTY_FONTS_AND_COLORS_ITEMS As String = "FontsAndColorsItems"


      Dim colProperties As EnvDTE.Properties
      Dim objValue As Object
      Dim bResult As Boolean = False

      Try

         colProperties = objDTE.Properties(CATEGORY_FONTS_AND_COLORS, PAGE_TEXT_EDITOR)

         If Not (colProperties Is Nothing) Then

            objValue = colProperties.Item(PROPERTY_FONT_FAMILY).Value
            r_sFontFamily = objValue.ToString

            objValue = colProperties.Item(PROPERTY_FONT_SIZE).Value
            r_sngFontSize = CType(objValue, Single)

            objValue = colProperties.Item(PROPERTY_FONTS_AND_COLORS_ITEMS).Object
            r_colFontsAndColorsItems = DirectCast(objValue, EnvDTE.FontsAndColorsItems)

            bResult = True

         End If

      Catch objException As Exception
         MessageBox.Show(objException.ToString)
      End Try

      Return bResult

   End Function

   Public Sub GetColorsFromFontsAndColorsItems(ByVal colFontsAndColorsItems As EnvDTE.FontsAndColorsItems, ByVal sDisplayItem As String, ByRef r_objBackColor As Color, ByRef r_objForeColor As Color, ByRef r_bBold As Boolean)

      Dim colColorableItems As EnvDTE.ColorableItems
      Dim iOleColor As Integer

      colColorableItems = colFontsAndColorsItems.Item(sDisplayItem)

      iOleColor = System.Convert.ToInt32(colColorableItems.Background)
      r_objBackColor = System.Drawing.ColorTranslator.FromOle(iOleColor)

      iOleColor = System.Convert.ToInt32(colColorableItems.Foreground)
      r_objForeColor = System.Drawing.ColorTranslator.FromOle(iOleColor)

      r_bBold = colColorableItems.Bold

   End Sub

   Public Sub AddColorsToRtfHeader(ByVal objRtfHeaderStringBuilder As System.Text.StringBuilder, ByVal objBackColor As Color, ByVal objForeColor As Color)

      AddColorToRtfHeader(objRtfHeaderStringBuilder, objBackColor)
      AddColorToRtfHeader(objRtfHeaderStringBuilder, objForeColor)

   End Sub

   Public Sub AddColorsToRtfHeader(ByVal objRtfHeaderStringBuilder As System.Text.StringBuilder, ByVal colFontsAndColorsItems As EnvDTE.FontsAndColorsItems, ByVal sDisplayItem As String, ByRef r_objBackColor As Color, ByRef r_objForeColor As Color, ByRef r_bBold As Boolean)

      GetColorsFromFontsAndColorsItems(colFontsAndColorsItems, sDisplayItem, r_objBackColor, r_objForeColor, r_bBold)

      AddColorsToRtfHeader(objRtfHeaderStringBuilder, r_objBackColor, r_objForeColor)

   End Sub

   Public Sub AddColorToRtfHeader(ByVal objRtfHeaderStringBuilder As System.Text.StringBuilder, ByVal objColor As Color)

      objRtfHeaderStringBuilder.Append("\red")  '
      objRtfHeaderStringBuilder.Append(objColor.R.ToString)  '
      objRtfHeaderStringBuilder.Append("\green")  '
      objRtfHeaderStringBuilder.Append(objColor.G.ToString)  '
      objRtfHeaderStringBuilder.Append("\blue")  '
      objRtfHeaderStringBuilder.Append(objColor.B.ToString)  '
      objRtfHeaderStringBuilder.Append(";")
      objRtfHeaderStringBuilder.Append(ControlChars.Cr)

   End Sub

   Public Function EscapeSpecialRTFCharacters(ByVal sLine As String) As String

      Dim sResult As String

      sResult = sLine

      If Not (sResult Is Nothing) Then
         sResult = sResult.Replace("\", "\\")
         sResult = sResult.Replace("{", "\{")
         sResult = sResult.Replace("}", "\}")
      End If

      Return sResult

   End Function

   Public Sub GetRtfBeforeAndAfter(ByVal objPlainTextBackColor As Color, ByVal objPlainTextForeColor As Color, ByVal bPlainTextBold As Boolean, _
   ByVal sPlainTextBackColorIndex As String, ByVal sPlainTextForeColorIndex As String, _
   ByVal objTokenBackColor As Color, ByVal objTokenForeColor As Color, ByVal bTokenBold As Boolean, _
   ByVal sTokenBackColorIndex As String, ByVal sTokenForeColorIndex As String, _
   ByRef r_sRtfBefore As String, ByRef r_sRtfAfter As String)

      r_sRtfBefore = ""
      r_sRtfAfter = ""

      If Not objTokenBackColor.Equals(objPlainTextBackColor) Then
         r_sRtfBefore &= "\highlight" & sTokenBackColorIndex & " "
         r_sRtfAfter &= "\highlight" & sPlainTextBackColorIndex & " "
      End If

      If Not objTokenForeColor.Equals(objPlainTextForeColor) Then
         r_sRtfBefore &= "\cf" & sTokenForeColorIndex & " "
         r_sRtfAfter &= "\cf" & sPlainTextForeColorIndex & " "
      End If

      If bTokenBold <> bPlainTextBold Then

         If bTokenBold Then
            r_sRtfBefore &= "\b "
            r_sRtfAfter &= "\b0 "
         Else
            r_sRtfBefore &= "\b0 "
            r_sRtfAfter &= "\b "
         End If

      End If

   End Sub

   Public Function ColorizeTokenBetweenDelimiters(ByVal sInput As String, ByVal sDelimiter1 As String, ByVal sDelimiter2 As String, ByVal sRtfBefore As String, ByVal sRtfAfter As String, ByVal sRtfKeywordBefore As String, ByVal sRtfKeywordAfter As String) As String

      Dim iStartPositionSearch1 As Integer
      Dim iStartPositionSearch2 As Integer
      Dim iDelimiterPos1 As Integer
      Dim iDelimiterPos2 As Integer
      Dim sToken As String
      Dim objStringBuilder As New System.Text.StringBuilder()

      iStartPositionSearch1 = 0

      Do

         If iStartPositionSearch1 >= sInput.Length Then

            Exit Do

         Else

            ' Find the first delimiter
            iDelimiterPos1 = sInput.IndexOf(sDelimiter1, iStartPositionSearch1)

            If iDelimiterPos1 = -1 Then ' Not found

               ' Add the remaining string before exiting
               objStringBuilder.Append(sInput.Substring(iStartPositionSearch1))

               Exit Do

            Else

               ' Add the consumed string until the found hit
               If iDelimiterPos1 > iStartPositionSearch1 Then
                  objStringBuilder.Append(sInput.Substring(iStartPositionSearch1, iDelimiterPos1 - iStartPositionSearch1))
               End If

               ' Find the second delimiter
               iStartPositionSearch2 = iDelimiterPos1 + sDelimiter1.Length
               If iStartPositionSearch2 < sInput.Length Then
                  iDelimiterPos2 = sInput.IndexOf(sDelimiter2, iStartPositionSearch2)
               Else
                  iDelimiterPos2 = -1
               End If

               If iDelimiterPos2 = -1 Then
                  ' Not found, so we assume it means until the end of text.
                  ' This is the case of comments, which use Cr (carriage return) as delimiter2 as the token delimiter. We must
                  ' colorize the comment even if it is the last line and no carriage return is used.
                  sToken = sInput.Substring(iDelimiterPos1)
               Else
                  sToken = sInput.Substring(iDelimiterPos1, iDelimiterPos2 - iDelimiterPos1 + sDelimiter2.Length)
               End If

               ' Remove the existing keyword colorization that may exist
               If sRtfKeywordBefore <> "" Then
                  sToken = sToken.Replace(sRtfKeywordBefore, "")
                  sToken = sToken.Replace(sRtfKeywordAfter, "")
               End If

               ' Add the token and its colorization
               If sRtfBefore <> "" Then
                  objStringBuilder.Append(sRtfBefore)
               End If
               objStringBuilder.Append(sToken)
               If sRtfAfter <> "" Then
                  objStringBuilder.Append(sRtfAfter)
               End If

               If iDelimiterPos2 = -1 Then ' The delimiter was not found, so we are done
                  Exit Do
               Else ' Prepare for the next loop
                  iStartPositionSearch1 = iDelimiterPos2 + sDelimiter2.Length
               End If

            End If

         End If

      Loop

      Return objStringBuilder.ToString

   End Function

End Class

 

Posted by carlosq | 2 comment(s)

I am writing an article about creating setups for Visual Studio add-ins and testing I have found that if the namespace/class specified in the <FullClassName> tag of the .AddIn XML file does not match the actual namespace and connect class name in the source code, you get an obscure <Unknown error> (error number 80131522) loading the add-in. Searching the web I found in the forums that I was not the first developer with this problem, so I have updated my article to reflect this:

HOWTO: Troubleshooting Visual Studio and Office add-ins
http://www.mztools.com/articles/2007/MZ2007009.aspx

It would be great if Microsoft could provide more helpful message errors when things are not set up as expected...

Posted by carlosq | with no comments

Since I suppose that somebody will request me the source code of the colorizer that I mentioned in my last post, the code is below. Just create a form with a RichTextBox control. Since this is not a "colorize as you type" approach (I think that the performance could suffer), you need to use the LostFocus of the RichTextBox control or use a "Colorize" button to call the ColorizeRichTextBox method passing the DTE instance, the RichTextBox control, the comment line prefix ( ' for VB.NET, // for C#) and the list of keywords of the language (see Visual Basic Language Keywords and C# Keywords). Notice that the RichTextBox of .NET 1.x doesn't support the SelectionBackColor property. The version that I am posting colorizes the plain text, keywords, strings and comments and it does a pretty good job resembling the code editor appearance. If you find bugs or enhancements let me know.

Public Class FormCodeColorizer

   Private Sub ButtonColorize_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonColorize.Click

      Dim objDTE As EnvDTE.DTE 

      objDTE = ... ' Get the EnvDTE.DTE from somewhere

      ColorizeRichText(objDTE, Me.RichTextBoxCode, "'", GetVBKeywords())

   End Sub

   Public Sub ColorizeRichText(ByVal objDTE As EnvDTE.DTE, ByVal ctlRichTextBox As RichTextBox, _
      ByVal sCommentLinePrefix As String, ByVal colKeywords As System.Collections.Specialized.StringCollection)

      Const DISPLAY_ITEM_PLAIN_TEXT As String = "Plain Text"
      Const DISPLAY_ITEM_KEYWORD As String = "Keyword"
      Const DISPLAY_ITEM_STRING As String = "String"

#If VS_8_0 Then
      ' PATCH: The Comment default color is wrong. We use the CSS Comment color instead
      Const DISPLAY_ITEM_COMMENT As String = "CSS Comment"
#Else
      Const DISPLAY_ITEM_COMMENT As String = "Comment"
#End If

      Dim objFont As Font = Nothing
      Dim objBackColor As Color
      Dim objTextColor As Color

      Dim iSelectionStart As Integer
      Dim iSelectionLength As Integer

      Dim colFontsAndColorsItems As EnvDTE.FontsAndColorsItems = Nothing
      Dim sFontFamily As String = ""
      Dim sngFontSize As Single
      Dim bBoldFont As Boolean

      If GetTextEditorFontAndColorsItems(objDTE, sFontFamily, sngFontSize, colFontsAndColorsItems) Then

         If GetFontAndColorInformation(colFontsAndColorsItems, DISPLAY_ITEM_PLAIN_TEXT, bBoldFont, objBackColor, objTextColor) Then

            Try

               If bBoldFont Then
                  objFont = New Font(sFontFamily, sngFontSize, FontStyle.Bold)
               Else
                  objFont = New Font(sFontFamily, sngFontSize)
               End If

               ' Cache selection position and length
               iSelectionStart = ctlRichTextBox.SelectionStart
               iSelectionLength = ctlRichTextBox.SelectionLength

               ' Set the properties of the control
               ctlRichTextBox.Font = objFont

               ctlRichTextBox.BackColor = objBackColor
               ctlRichTextBox.ForeColor = objTextColor

               ' This is to reset the font and color properties, just in case there was previous colorizing information
               ctlRichTextBox.SelectionStart = 0
               ctlRichTextBox.SelectionLength = ctlRichTextBox.TextLength
               ctlRichTextBox.SelectionFont = objFont
               ctlRichTextBox.SelectionColor = objTextColor
               ctlRichTextBox.SelectionBackColor = objBackColor

               ' Colorize keywords
               ColorizeRichTextKeywords(ctlRichTextBox, colKeywords, sFontFamily, sngFontSize, colFontsAndColorsItems, DISPLAY_ITEM_KEYWORD)

               ' Colorize comments
               ColorizeRichTextTokens(ctlRichTextBox, sCommentLinePrefix, ControlChars.Cr, RichTextBoxFinds.None, sFontFamily, sngFontSize, colFontsAndColorsItems, DISPLAY_ITEM_COMMENT)

               ' Colorize strings
               ColorizeRichTextTokens(ctlRichTextBox, ControlChars.Quote, ControlChars.Quote, RichTextBoxFinds.None, sFontFamily, sngFontSize, colFontsAndColorsItems, DISPLAY_ITEM_STRING)

               ' Restore selection position and length
               ctlRichTextBox.SelectionStart = iSelectionStart
               ctlRichTextBox.SelectionLength = iSelectionLength

            Finally

               If Not (objFont Is Nothing) Then
                  objFont.Dispose()
               End If

            End Try

         End If

      End If

   End Sub

   Public Function GetVBKeywords() As System.Collections.Specialized.StringCollection

      Dim colVBKeywords As New System.Collections.Specialized.StringCollection

      colVBKeywords.Add("AddHandler")
      colVBKeywords.Add("AddressOf")
      colVBKeywords.Add("Alias")
      colVBKeywords.Add("And")
      colVBKeywords.Add("AndAlso")
      colVBKeywords.Add("As")
      colVBKeywords.Add("Boolean")
      colVBKeywords.Add("ByRef")
      colVBKeywords.Add("Byte")
      colVBKeywords.Add("ByVal")
      colVBKeywords.Add("Call")
      colVBKeywords.Add("Case")
      colVBKeywords.Add("Catch")
      colVBKeywords.Add("CBool")
      colVBKeywords.Add("CByte")
      colVBKeywords.Add("CChar")
      colVBKeywords.Add("CDate")
      colVBKeywords.Add("CDec")
      colVBKeywords.Add("CDbl")
      colVBKeywords.Add("Char")
      colVBKeywords.Add("CInt")
      colVBKeywords.Add("Class")
      colVBKeywords.Add("CLng")
      colVBKeywords.Add("CObj")
      colVBKeywords.Add("Const")
      colVBKeywords.Add("Continue")
      colVBKeywords.Add("CSByte")
      colVBKeywords.Add("CShort")
      colVBKeywords.Add("CSng")
      colVBKeywords.Add("CStr")
      colVBKeywords.Add("CType")
      colVBKeywords.Add("CUInt")
      colVBKeywords.Add("CULng")
      colVBKeywords.Add("CUShort")
      colVBKeywords.Add("Date")
      colVBKeywords.Add("Decimal")
      colVBKeywords.Add("Declare")
      colVBKeywords.Add("Default")
      colVBKeywords.Add("Delegate")
      colVBKeywords.Add("Dim")
      colVBKeywords.Add("DirectCast")
      colVBKeywords.Add("Do")
      colVBKeywords.Add("Double")
      colVBKeywords.Add("Each")
      colVBKeywords.Add("Else")
      colVBKeywords.Add("ElseIf")
      colVBKeywords.Add("End")
      colVBKeywords.Add("EndIf")
      colVBKeywords.Add("Enum")
      colVBKeywords.Add("Erase")
      colVBKeywords.Add("Error")
      colVBKeywords.Add("Event")
      colVBKeywords.Add("Exit")
      colVBKeywords.Add("False")
      colVBKeywords.Add("Finally")
      colVBKeywords.Add("For")
      colVBKeywords.Add("Friend")
      colVBKeywords.Add("Function")
      colVBKeywords.Add("Get")
      colVBKeywords.Add("GetType")
      colVBKeywords.Add("GetXMLNamespace")
      colVBKeywords.Add("Global")
      colVBKeywords.Add("GoSub")
      colVBKeywords.Add("GoTo")
      colVBKeywords.Add("Handles")
      colVBKeywords.Add("If")
      colVBKeywords.Add("Implements")
      colVBKeywords.Add("Imports")
      colVBKeywords.Add("In")
      colVBKeywords.Add("Inherits")
      colVBKeywords.Add("Integer")
      colVBKeywords.Add("Interface")
      colVBKeywords.Add("Is")
      colVBKeywords.Add("IsNot")
      colVBKeywords.Add("Let")
      colVBKeywords.Add("Lib")
      colVBKeywords.Add("Like")
      colVBKeywords.Add("Long")
      colVBKeywords.Add("Loop")
      colVBKeywords.Add("Me")
      colVBKeywords.Add("Mod")
      colVBKeywords.Add("Module")
      colVBKeywords.Add("MustInherit")
      colVBKeywords.Add("MustOverride")
      colVBKeywords.Add("MyBase")
      colVBKeywords.Add("MyClass")
      colVBKeywords.Add("Namespace")
      colVBKeywords.Add("Narrowing")
      colVBKeywords.Add("New")
      colVBKeywords.Add("Next")
      colVBKeywords.Add("Not")
      colVBKeywords.Add("Nothing")
      colVBKeywords.Add("NotInheritable")
      colVBKeywords.Add("NotOverridable")
      colVBKeywords.Add("Object")
      colVBKeywords.Add("Of")
      colVBKeywords.Add("On")
      colVBKeywords.Add("Operator")
      colVBKeywords.Add("Option")
      colVBKeywords.Add("Optional")
      colVBKeywords.Add("Or")
      colVBKeywords.Add("OrElse")
      colVBKeywords.Add("Overloads")
      colVBKeywords.Add("Overridable")
      colVBKeywords.Add("Overrides")
      colVBKeywords.Add("ParamArray")
      colVBKeywords.Add("Partial")
      colVBKeywords.Add("Private")
      colVBKeywords.Add("Property")
      colVBKeywords.Add("Protected")
      colVBKeywords.Add("Public")
      colVBKeywords.Add("RaiseEvent")
      colVBKeywords.Add("ReadOnly")
      colVBKeywords.Add("ReDim")
      colVBKeywords.Add("REM")
      colVBKeywords.Add("RemoveHandler")
      colVBKeywords.Add("Resume")
      colVBKeywords.Add("Return")
      colVBKeywords.Add("SByte")
      colVBKeywords.Add("Select")
      colVBKeywords.Add("Set")
      colVBKeywords.Add("Shadows")
      colVBKeywords.Add("Shared")
      colVBKeywords.Add("Short")
      colVBKeywords.Add("Single")
      colVBKeywords.Add("Static")
      colVBKeywords.Add("Step")
      colVBKeywords.Add("Stop")
      colVBKeywords.Add("String")
      colVBKeywords.Add("Structure")
      colVBKeywords.Add("Sub")
      colVBKeywords.Add("SyncLock")
      colVBKeywords.Add("Then")
      colVBKeywords.Add("Throw")
      colVBKeywords.Add("To")
      colVBKeywords.Add("True")
      colVBKeywords.Add("Try")
      colVBKeywords.Add("TryCast")
      colVBKeywords.Add("TypeOf")
      colVBKeywords.Add("Variant")
      colVBKeywords.Add("Wend")
      colVBKeywords.Add("UInteger")
      colVBKeywords.Add("ULong")
      colVBKeywords.Add("UShort")
      colVBKeywords.Add("Using")
      colVBKeywords.Add("When")
      colVBKeywords.Add("While")
      colVBKeywords.Add("Widening")
      colVBKeywords.Add("With")
      colVBKeywords.Add("WithEvents")
      colVBKeywords.Add("WriteOnly")
      colVBKeywords.Add("Xor")
      colVBKeywords.Add("#Const")
      colVBKeywords.Add("#Else")
      colVBKeywords.Add("#ElseIf")
      colVBKeywords.Add("#End")
      colVBKeywords.Add("#If")
      colVBKeywords.Add("=")
      colVBKeywords.Add("&")
      colVBKeywords.Add("&=")
      colVBKeywords.Add("*")
      colVBKeywords.Add("*=")
      colVBKeywords.Add("/")
      colVBKeywords.Add("/=")
      colVBKeywords.Add("\")
      colVBKeywords.Add("\=")
      colVBKeywords.Add("^")
      colVBKeywords.Add("^=")
      colVBKeywords.Add("+")
      colVBKeywords.Add("+=")
      colVBKeywords.Add("-")
      colVBKeywords.Add("-=")
      colVBKeywords.Add(">>")
      colVBKeywords.Add(">>=")
      colVBKeywords.Add("<<")
      colVBKeywords.Add("<<=")

      Return colVBKeywords

   End Function

   Public Sub ColorizeRichTextKeywords(ByVal ctlRichTextBox As RichTextBox, _
      ByVal colKeywords As System.Collections.Specialized.StringCollection, _
      ByVal sFontFamily As String, ByVal sngFontSize As Single, _
      ByVal colFontsAndColorsItems As EnvDTE.FontsAndColorsItems, ByVal sDisplayItem As String)

      Dim sKeyword As String
      Dim eRichTextBoxFinds As RichTextBoxFinds

      eRichTextBoxFinds = RichTextBoxFinds.WholeWord Or RichTextBoxFinds.MatchCase

      For Each sKeyword In colKeywords

         ColorizeRichTextTokens(ctlRichTextBox, sKeyword, "", eRichTextBoxFinds, sFontFamily, sngFontSize, colFontsAndColorsItems, sDisplayItem)

      Next

   End Sub

   Public Sub ColorizeRichTextTokens(ByVal ctlRichTextBox As RichTextBox, _
      ByVal sToken As String, ByVal sOptionalTokenDelimiter As String, _
      ByVal eRichTextBoxFinds As RichTextBoxFinds, ByVal sFontFamily As String, ByVal sngFontSize As Single, _
      ByVal colFontsAndColorsItems As EnvDTE.FontsAndColorsItems, ByVal sDisplayItem As String)

      Dim iPosition1 As Integer
      Dim iPosition2 As Integer
      Dim objFont As Font = Nothing
      Dim objBackColor As Color
      Dim objTextColor As Color
      Dim bBoldFont As Boolean
      Dim bFontAndColorInformationRetrieved As Boolean = False

      Try

         If Not (colFontsAndColorsItems Is Nothing) Then

            If sToken <> "" Then

               iPosition1 = 0

               Do

                  iPosition1 = ctlRichTextBox.Find(sToken, iPosition1, eRichTextBoxFinds)
                  If iPosition1 = -1 Then
                     Exit Do
                  Else

                     If sOptionalTokenDelimiter = "" Then
                        iPosition2 = iPosition1 + sToken.Length
                     Else

                        If iPosition1 + sToken.Length < ctlRichTextBox.TextLength Then
                           iPosition2 = ctlRichTextBox.Find(sOptionalTokenDelimiter, iPosition1 + sToken.Length, RichTextBoxFinds.None)
                        Else
                           iPosition2 = -1
                        End If

                        If iPosition2 = -1 Then
                           ' Not found, but the delimiter was passed so we assume it means until the end of text.
                           ' This is the case of comments, which use Cr (carriage return) as the token delimiter. We must
                           ' colorize the comment even if it is the last line and no carriage return is used.
                           ' This does not apply to keywords because they don't use token delimiter, so it works as expected
                           iPosition2 = ctlRichTextBox.TextLength
                        End If
                     End If

                     ' Resources are not created until an occurrence was found. And then, they are created only once
                     If Not bFontAndColorInformationRetrieved Then

                        If GetFontAndColorInformation(colFontsAndColorsItems, sDisplayItem, bBoldFont, objBackColor, objTextColor) Then
                           If sFontFamily <> "" Then
                              If bBoldFont Then
                                 objFont = New Font(sFontFamily, sngFontSize, FontStyle.Bold)
                              Else
                                 objFont = New Font(sFontFamily, sngFontSize)
                              End If
                              bFontAndColorInformationRetrieved = True
                           End If
                        End If

                     End If

                     If Not bFontAndColorInformationRetrieved Then
                        Exit Do
                     Else
                        ctlRichTextBox.SelectionStart = iPosition1
                        ctlRichTextBox.SelectionLength = iPosition2 - iPosition1 + sOptionalTokenDelimiter.Length

                        If Not (objFont Is Nothing) Then
                           ctlRichTextBox.SelectionFont = objFont
                        End If

                        If Not objTextColor.Equals(Color.Empty) Then
                           ctlRichTextBox.SelectionColor = objTextColor
                        End If

                        ' Note: The RichTextBox of .NET 1.x doesn't support the SelectionBackColor property
                        If Not objBackColor.Equals(Color.Empty) Then
                           ctlRichTextBox.SelectionBackColor = objBackColor
                        End If

                        iPosition1 = iPosition2 + sOptionalTokenDelimiter.Length

                        If iPosition1 >= ctlRichTextBox.TextLength Then
                           Exit Do
                        End If

                     End If

                  End If

               Loop

            End If

         End If

      Finally

         If Not (objFont Is Nothing) Then
            objFont.Dispose()
         End If

      End Try

   End Sub

   Public Function GetFontAndColorInformation(ByVal colFontsAndColorsItems As EnvDTE.FontsAndColorsItems, _
      ByVal sDisplayItem As String, ByRef r_bBoldFont As Boolean, ByRef r_objBackColor As Color, _
      ByRef r_objTextColor As Color) As Boolean

      Dim colColorableItems As EnvDTE.ColorableItems
      Dim iOleColor As Integer
      Dim bResult As Boolean = False

      Try

         If Not (colFontsAndColorsItems Is Nothing) Then

            colColorableItems = colFontsAndColorsItems.Item(sDisplayItem)

            iOleColor = System.Convert.ToInt32(colColorableItems.Background)
            r_objBackColor = System.Drawing.ColorTranslator.FromOle(iOleColor)

            iOleColor = System.Convert.ToInt32(colColorableItems.Foreground)
            r_objTextColor = System.Drawing.ColorTranslator.FromOle(iOleColor)

            r_bBoldFont = colColorableItems.Bold

            bResult = True

         End If

      Catch objException As Exception
         MessageBox.Show(objException.ToString)
      End Try

      Return bResult

   End Function

   Public Function GetTextEditorFontAndColorsItems(ByVal objDTE As EnvDTE.DTE, ByRef r_sFontFamily As String, _
      ByRef r_sngFontSize As Single, ByRef r_colFontsAndColorsItems As EnvDTE.FontsAndColorsItems) As Boolean

      Const CATEGORY_FONTS_AND_COLORS As String = "FontsAndColors"
      Const PAGE_TEXT_EDITOR As String = "TextEditor"

      Const PROPERTY_FONT_SIZE As String = "FontSize"
      Const PROPERTY_FONT_FAMILY As String = "FontFamily"
      Const PROPERTY_FONTS_AND_COLORS_ITEMS As String = "FontsAndColorsItems"


      Dim colProperties As EnvDTE.Properties
      Dim objValue As Object
      Dim bResult As Boolean = False

      Try

         colProperties = objDTE.Properties(CATEGORY_FONTS_AND_COLORS, PAGE_TEXT_EDITOR)

         If Not (colProperties Is Nothing) Then

            objValue = colProperties.Item(PROPERTY_FONT_FAMILY).Value
            r_sFontFamily = objValue.ToString

            objValue = colProperties.Item(PROPERTY_FONT_SIZE).Value
            r_sngFontSize = CType(objValue, Single)

            objValue = colProperties.Item(PROPERTY_FONTS_AND_COLORS_ITEMS).Object
            r_colFontsAndColorsItems = DirectCast(objValue, EnvDTE.FontsAndColorsItems)

            bResult = True

         End If

      Catch objException As Exception
         MessageBox.Show(objException.ToString)
      End Try

      Return bResult

   End Function

End Class

Posted by carlosq | 1 comment(s)

Although I wrote long time ago an article about EnvDTE.DTE.Properties, it was not until this week that I needed to retrieve font and color information of the text editor of Visual Studio. Basically I am changing the code template editor of my MZ-Tools add-in from black and white to colorized text using a RichTextBox, resembling as much as possible the code editor of VS without using more complex 3rd party products (such as Activepro Syntaxt Editor, which seems excellent) or trying to host the Visual Studio code editor inside a Windows Form.

Here is a new article about this subject and some things that I have encountered.

HOWTO: Get the text editor font and colors information in a Visual Studio add-in
http://www.mztools.com/articles/2008/MZ2008008.aspx

BTW, notice that VS 2005 has a bug (fixed in VS 2008) and the "Comment" foreground color is not returned correctly (although the VS code editor colorizes perfectly). You may want to use the XML Comment or CSS Comment foreground color in this case.

Posted by carlosq | with no comments

This is a really tricky one that I found in the forums months ago and I have documented it today:

PRB: CodeModelEvents not firing events in a Visual Studio add-in
http://www.mztools.com/Articles/2008/MZ2008007.aspx

Posted by carlosq | with no comments

I added yesterday a new resource to the "Resources about Visual Studio extensibility" section of my web site that may be interesting for those of you starting with VS SDK packages:

http://www.architekturaforum.hu/blogs/divedeeper/archive/2008/01/02/LearnVSXNowPart1.aspx

Posted by carlosq | 1 comment(s)

As you may have noticed, there aren't many books on Visual Studio Extensibility, so any new book is great news. This is the case of a new book with the title "Professional Visual Studio Extensibility" by Keyvan Nayyeri:

http://nayyeri.net/blog/professional%2Dvisual%2Dstudio%2Dextensibility%2Dfinally%2Dreleased/

It is already available from Wiley and Wrox (where you can read the TOC, index, and first chapter) and will be available from Amazon shortly. I haven't read it yet (I am awaiting a complimentary copy in a few days or weeks) but I am excited because this book covers more than add-ins or macros, so you have chapters about VS SDK packages, DSL tools, Visual Studio Shell, extending the debugger or even MSBuild. While there is lot of information about those subjects in MSDN and blogs, I personally prefer a good book to learn new technologies.

 

Posted by carlosq | 3 comment(s)

This is another question that appears from time to time in the forums, and it is one extremely complicated to have a 100% satisfaction with the result :-). I haven't written a MZ-Tools Series article for this since I don't have a comprehensive answer with a sample code, but I will ellaborate a bit about this in this post.

The question is, suppose I have a string like "Car" that I know it's a type. How can I get the System.Type that represents that type? Before that question there is a previous one: how did I get that string? There are several ways:

 - Using the code model (EnvDTE.Project.CodeModel or EnvDTE.ProjectItem.FileCodeModel), you have a CodeVariable, CodeParameter, etc. which has a CodeTypeRef property that returns the type of the code element.

- Parsing a procedure code (by hand, since the code model doesn't cover that), you get statements like "Dim objCar as Car" (VB.NET) or "Car mycar" (C#), so somehow you get the "Car" string and you guess that it is a type.

There are a couple of complications to note here:

 - Types have a short name, such as "Car", and a fully qualified name, such as "Vehicles.Motorized.Car". Since you can have "Imports" statements at file or project level in VB.NET and "using" statements at file level in C#, it is not required that the fully qualified name to appear in the variable or parameter declaration. Note: even if the declaration contains a dot (.) you can't assure that it is a fully qualified name because it can be a partially-qualified name, such as if you import the "Vehicles" namespace at file level and you declare the variable as Dim objCar As Motorized.Car.

 - Types can reside in compiled assemblies references, or, alas, in the project's source code or in a project (not compiled) reference. In the last two cases, you can't have a System.Type, since maybe the project is not compiled yet (first time) or maybe the last compilation was days ago and it is not up-to-date. In these cases, for most accuracy you don't really want a System.Type but an EnvDTE.CodeElement such as an EnvDTE.CodeClass, EnvDTE.CodeStruct, EnvDTE.CodeInterface, etc. So, your add-in has to deal with two kind of types: compiled types (represented by a System.Type) and source code types (represented by a CodeElement).

So, let's try to solve them:

 - To get the fully qualified name of a type name, assumming that it comes from a EnvDTE.CodeTypeRef, you can try the CodeTypeRef.AsFullName property (in contrast to its CodeTypeRef.AsString property). I don't know how reliable is this in each version of Visual Studio and for each language (VB.NET, C#) but my experience is that you can't fully trust the code model. As a workaround, you should compose a list of fully qualified candidate type names combining the imported namespaces and then try to resolve which of them is the true one. This is what the compiler does after all when building the project, although it doesn't expose that information to add-ins or packages (AFAIK).

- Given the fully qualified name, or a list of candidates, you need to "resolve" them to a System.Type or EnvDTE.CodeElement. To do this, I would start with compiled references since is faster than using the code model. You would have to get the references of the project which are compiled assemblies (see HOWTO: Getting information specific to VB.NET and C# projects from an add-in or macro). For each compiled reference you get the name of the assembly and using Reflection you can use Assembly.Load or Assembly.LoadFrom to load it and then call Assembly.GetExportedTypes to get the public types and then iterate them until you find the one that matches the fully qualified type name. If you don't find it, then chances are that it is a type declared in the source code of the project, or in one of the project (not compiled) references. In these cases you have to navigate the code model of each project (see HOWTO: Navigate the files of a solution from a Visual Studio .NET macro or add-in and HOWTO: Navigate the code elements of a file from a Visual Studio .NET macro or add-in). Needless to say, this can be slow.

All that said, you may have heard of the ITypeResolutionService service of Visual Studio, which sounds promising to avoid all that. I haven't tried it personally because it was not clear for me how to get an instance of it (most documentation refers to component designers) but today I found two posts of MVP fellow Daniel Cazzulino that can be helpful:

Retrieving available types in current project and its references (withoult locking) 
http://www.clariusconsulting.net/blogs/kzu/archive/2006/01/06/GetTypesFromProject.aspx

and

How to get a System.Type from an EnvDTE.CodeTypeRef or EnvDTE.CodeClass
http://www.clariusconsulting.net/blogs/kzu/archive/2007/10/04/34019.aspx

So, he explains how to get the all the types declared in a project and references using the ITypeDiscoveryService, and how to get a System.Type from a type name using the ITypeResolutionService, retrieving both services from an EnvDTE.Project. These is great news, but notice the caveat that for types declared in source code, it will retrieve a System.Type from the last successful compilation (if any) and not an EnvDTE.CodeElement. Assuming that you don't mind the issue of the out-of-date compilation, getting a System.Type can be enough for some scenarios, but not for others, such as when you want to go to the source code file of the type (think the Go To Definition command of Visual Studio), where you really want an EnvDTE.CodeElement (which contains the ProjectItem and location, etc.) and not a System.Type.

 

 

Posted by carlosq | 3 comment(s)

Someone asked today something related to this in the MSDN forum and since I remembered being there and that the solution was not very intuitive, I wrote this article:

HOWTO: Add an event handler from a Visual Studio add-in
http://www.mztools.com/articles/2008/MZ2008006.aspx

Posted by carlosq | with no comments

This week I attended the TechDays 2008 here at Madrid, a two-days event to launch Visual Studio 2008, Windows Server 2008 and SQL Server 2008 (this product actually is not released yet but...). I don't know if it was that Microsoft Iberica (the subsidiary for Spain and Portugal) celebrated its 20 anniversary or that the new products deserved it, but this has been the greatest Microsoft event that I ever attended (except the Microsoft TechEd at Barcelona that I attend every some years). It was like a mini-TechEd, with lots of parallel sessions, hands-on labs, ask the expert, partners expo, etc. and even hot dogs ;-). Lots of known people (many speakers were fellow MVPs) and very good content in most sessions. Apart from the new exciting technologies such as WCF, WFF, WPF or SilverLight I was mostly interested in Team Foundation Server 2008, a great tool for collaboration and team work that hopefully I will install and use this year, and which furthermore provides extensibility, so who knows if I enter that area some day...

Posted by carlosq | 2 comment(s)

Microsoft announced it some days ago: the new Visual Studio Gallery (http://www.visualstudiogallery.com), a new web site with lots of 3rd party products for Visual Studio, such as controls, source control providers, add-ins, packages, etc. Considering how hard can be sometimes the Visual Studio extensibility, it's great to see the big ecosystem of tools that many people is building around Microsoft Visual Studio. The Eclipse IDE for Java has had always such a big ecosystem of plugins (I used Eclipse with some plugins to develop a J2ME game for Java-enabled mobile phones a couple of years ago) and now Visual Studio has such ecosystem in a centralized website, easy to find and navigate.

Of course, my MZ-Tools add-in is part of such ecosystem:

http://www.visualstudiogallery.com/ExtensionDetails.aspx?ExtensionId=3b070c5d-6478-4a86-aafa-026e79189e9e

Posted by carlosq | 1 comment(s)