ENTERPRISE

Visual Studio 2010 : Introducing the Visual Studio Extensibility - Building a Visual Studio Package

6/9/2012 4:45:25 PM
The Visual Studio extensibility is showing how you create and deploy a Visual Studio Package. Basically Visual Studio is made of packages. Each package represents a working unit. For example, the toolbox is a package; Solution Explorer is another package, and so on. You can extend Visual Studio by building custom integration packages. There are different kinds of integration packages, such as tool windows, menus, wizards, and languages. The big difference between a package and an add-in is that a package can add completely new tools and features to the IDE, whereas add-ins typically extend existing features in the IDE with other features. In the next code example you learn to build a custom tool window that can provide the ability of compiling code snippets on-the-fly in both Visual Basic and Visual C#. Open the New Project window by selecting File, New Project. When the New Project window appears, select the Other Project Types, Extensibility subfolder on the left and then the Visual Studio Integration Package project template. Name the new project SnippetCompilerVSPackage (see Figure 1) and then click OK.
Figure 1. Creating the new extensibility project.

At this point the Visual Studio Integration Package Wizard starts. In the first step select Visual Basic as the programming language and leave unchanged the new key file option, as shown in Figure 2.

Figure 2. Setting language and key file options.

In the next window you can set information for the new package, such as author, description, and icon. Figure 3 shows an example for these settings.

Figure 3. Setting package information.

The next step is important and is the place where you can select the package type. Select Tool Window and then click Next (see Figure 4). You can also select multiple options depending on where you want the tool to be available.

Figure 4. Package type selection.

In the next step, represented in Figure 5, you can specify the Window name and Command ID. The Window name is actually the tool window title, whereas the ID is used internally by Visual Studio for invoking the new package. Figure 5 shows an example about setting the information.

Figure 5. Setting title and ID for the new tool window.

The next step requires you to specify if you want to add test projects for the new package. For this example, uncheck both projects and proceed. At this point you can complete the wizard and Visual Studio will generate the new project. When the new project is ready, the first thing you notice is that, differently from previous version, the tool window is nothing but a WPF custom control. Now double-click the MyControl.xaml file in Solution Explorer, in order to enable the designer. Figure 6 shows how the IDE appears at this point.

Figure 6. The IDE is ready on the new extensibility project, showing the WPF custom control.

Visual Studio implements a WPF skeleton for a new tool window that you need to customize. Before going into that, consider the meaning of files available within Solution Explorer. This is summarized in Table 1.

Table 1. VS Package Code Files
FileDescription
Guids.vbDefines a number of GUIDs that Visual Studio will utilize to recognize and implement the tool window
MyToolWindow.vbA class implementing the tool window hosting it as a user control
PkgCmdId.vbExposes a unique identifier for the package within Visual Studio
Resources.resxExposes resources required by the IDE
VSPackage.resxExposes resources required by the package
MyControl.XamlThe WPF custom control actually implementing the tool window content
SnippetCompilerVsPackagePackage.vbThe class implementing the tool window
SnippetCompilerVsPackage.vsctAn xml file defining the layout of the package, including company information
Source.extension.vsixmanifestAn xml file used for deploying packages as a .Vsix file (see later in this chapter)
Key.snkStrong name file required for signing the package assembly

There is also a subfolder named Resources that contains the icons used within the package and that identifies the new tool in Visual Studio. All code files contain comments that can help you understand what that particular file is for. For example, take a look at the SnippetCompilerVsPackagePackage.vb file. For your convenience, Listing 1 shows the content of this file. Notice how comments are detailed and how they provide complete explanations on types and their role within the user interface.

Listing 1. Understanding Packages Behind the Scenes
Imports Microsoft.VisualBasic
Imports System
Imports System.Diagnostics
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.ComponentModel.Design
Imports Microsoft.Win32
Imports Microsoft.VisualStudio.Shell.Interop
Imports Microsoft.VisualStudio.OLE.Interop
Imports Microsoft.VisualStudio.Shell

''' <summary>
''' This is the class that implements the package exposed by this assembly.
'''
''' The minimum requirement for a class to be considered a valid package for
''' Visual Studio is to implement the IVsPackage interface and register itself with
''' the shell.
''' This package uses the helper classes defined inside the
''' Managed Package Framework (MPF)
''' to do it: it derives from the Package class that provides the implementation of
''' the IVsPackage interface and uses the registration attributes defined in the
''' ''' framework to register itself and its components with the shell.
''' </summary>
' The PackageRegistration attribute tells the PkgDef creation utility
' (CreatePkgDef.exe) that this class is a package.
'
' The InstalledProductRegistration attribute is used to register the information
needed to show this package
' in the Help/About dialog of Visual Studio.
    '
' The ProvideMenuResource attribute is needed to let the shell know that this
' package exposes some menus.
' The ProvideToolWindow attribute registers a tool window exposed by this package.

    <PackageRegistration(UseManagedResourcesOnly := true), _
    InstalledProductRegistration("#110", "#112", "1.0", IconResourceID := 400), _
    ProvideMenuResource("Menus.ctmenu", 1), _
    ProvideToolWindow(GetType(MyToolWindow)), _
    Guid(GuidList.guidSnippetCompilerVSPackagePkgString)> _
    Public NotInheritable Class SnippetCompilerVSPackagePackage
Inherits Package
''' <summary>
''' Default constructor of the package.
''' Inside this method you can place any initialization code that does not require
''' any Visual Studio service because at this point the package object is created
''' but not sited yet inside Visual Studio environment. The place to do all the
''' other initialization is the Initialize method.
''' </summary>
Public Sub New()
        Trace.WriteLine(String.Format(CultureInfo.CurrentCulture,
                                     "Entering constructor for: {0}",
Me.GetType().Name))
End Sub
        ''' <summary>
''' This function is called when the user clicks the menu item that shows the
''' tool window. See the Initialize method to see how the menu item is associated to
''' this function using the OleMenuCommandService service and the MenuCommand class.
''' </summary>
Private Sub ShowToolWindow(ByVal sender As Object, ByVal e As EventArgs)
    ' Get the instance number 0 of this tool window. This window is single instance
    ' so this instance
    ' is actually the only one.
    ' The last flag is set to true so that if the tool window does not exists it
    ' will be created.
    Dim window As ToolWindowPane = Me.FindToolWindow(GetType(MyToolWindow), 0, True)
    If (window Is Nothing) Or (window.Frame Is Nothing) Then
        Throw New NotSupportedException(Resources.CanNotCreateWindow)
    End If

    Dim windowFrame As IVsWindowFrame = TryCast(window.Frame, IVsWindowFrame)
    Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show())
End Sub


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Overriden Package Implementation
#Region "Package Members"

''' <summary>
''' Initialization of the package; this method is called right
''' after the package is sited, so this is the place
''' where you can put all the initilaization code that rely on services provided by
''' VisualStudio.
''' </summary>
Protected Overrides Sub Initialize()
       Trace.WriteLine(String.Format(CultureInfo.CurrentCulture,
                                     "Entering Initialize() of: {0}",
                                     Me.GetType().Name))
    MyBase.Initialize()

        ' Add our command handlers for menu (commands must exist in the .vsct file)
        Dim mcs As OleMenuCommandService = _
            TryCast(GetService(GetType(IMenuCommandService)), OleMenuCommandService)
    If Not mcs Is Nothing Then
                ' Create the command for the tool window
            Dim toolwndCommandID As New CommandID(GuidList.
                                    guidSnippetCompilerVSPackageCmdSet,
                                    CInt(PkgCmdIDList.cmdidSnippetCompiler))
            Dim menuToolWin As New MenuCommand(New EventHandler _
                               (AddressOf ShowToolWindow), toolwndCommandID)
        mcs.AddCommand(menuToolWin)
            End If
        End Sub
#End Region
End Class

					  

The class inherits from Microsoft.VisualStudio.Shell.Package, the base class exposing the required interface for every functional package. Notice how the ShowToolWindow method gets an instance of the Microsoft.VisualStudio.Shell.ToolWindowPane class pointing to the custom tool window (Me.FindToolWindow (GetType(MyToolWindow))). The same exam can be done on the ToolWindow.vb file. After doing this, it is possible to customize the WPF control. The goal of the tool window is enabling on-the fly compilation for code snippets. With that said, there is the need of implementing the user interface side, so in the XAML editor type the code shown in Listing 2.

Listing 2. Implementing the Tool Window User Interface
<UserControl x:Class="MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vsfx="clrnamespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.10.0"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300"
             Name="MyToolWindow"
             Background="{DynamicResource
             {x:Static vsfx:VsBrushes.ToolWindowBackgroundKey}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="40" />
            <RowDefinition />
            <RowDefinition Height="50" />
            <RowDefinition Height="40" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <!— This will allow selecting the compiler —>
        <ComboBox Name="LanguageCombo" Text="VisualBasic" Margin="5">
            <ComboBoxItem Content="VisualBasic" />
            <ComboBoxItem Content="CSharp" />
        </ComboBox>
        <TextBlock Margin="5" Grid.Row="1"
                       Foreground="{DynamicResource
                       {x:Static vsfx:VsBrushes.ToolWindowTextKey}}">
                Write or paste your code here:</TextBlock>
            <TextBox Grid.Row="2"  Name="CodeTextBox" Margin="5"
                     Foreground="{DynamicResource
                     {x:Static vsfx:VsBrushes.ToolWindowTextKey}}"
                     AcceptsReturn="True" AcceptsTab="True"
                     VerticalAlignment="Stretch"
                     VerticalScrollBarVisibility="Auto"
                     HorizontalScrollBarVisibility="Auto" />
            <Button Grid.Row="3" Content="Compile code" Width="80" Height="40"
                    Name="button1"/>
            <TextBlock Grid.Row="4" Margin="10"
                       Foreground="{DynamicResource
                       {x:Static vsfx:VsBrushes.ToolWindowTextKey}}">
                Compilation results:</TextBlock>
            <ListBox Grid.Row="5" ItemsSource="{Binding}"
                Name="ErrorsListBox" Margin="5"
                Foreground="{DynamicResource
                {x:Static vsfx:VsBrushes.ToolWindowTextKey}}"/>
    </Grid>
</UserControl>

					  

On the Visual Basic side, enter the MyControl.xaml.vb file and write the code shown in Listing 3. This adds compile functionalities to the tool window when the button is clicked. Basically the code makes use of the System.CodeDom namespace for getting instances of the .NET compilers, as you will understand through comments in the code.

Listing 3. Code for Compiling On-the-Fly the Code Typed Inside the Tool Window
Imports System.Security.Permissions
Imports System
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.CodeDom.Compiler
Imports System.Windows.Controls

'''<summary>
'''  Interaction logic for MyControl.xaml
'''</summary>
Partial Public Class MyControl
    Inherits System.Windows.Controls.UserControl

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",
                        "CA1300:SpecifyMessageBoxOptions")> _
    Private Sub button1_Click(ByVal sender As Object,
                              ByVal e As System.EventArgs) Handles button1.Click
        Try
            If String.IsNullOrEmpty(CodeTextBox.Text) = False Then

                Me.ErrorsListBox.ItemsSource = _
                Compile(CType(Me.LanguageCombo.SelectedItem,
                        ComboBoxItem).Content.ToString)

            End If
        Catch ex As Exception
            'Handle other exceptions here, no compiler errors
        End Try

    End Sub

    Private Function Compile(ByVal language As String) As IEnumerable(Of String)

        'Gets the ComboBox selected language
        Dim languageProvider As String = language

        'Creates an instance of the desired compiler
        Dim CompilerProvider As CodeDomProvider = _
            CodeDomProvider.CreateProvider(languageProvider)
        'Sets compiler parameters
        Dim params As New CompilerParameters()
        Dim results As CompilerResults

        'Configure self-explanatory parameters
        With params
            .GenerateExecutable = False
            .GenerateInMemory = True
            .IncludeDebugInformation = False
            'You can add multiple references here
            .ReferencedAssemblies.Add("System.dll")
        End With

        'Compiles the specified source code
        results = CompilerProvider.
                  CompileAssemblyFromSource(params,
                                            CodeTextBox.Text)

        'If no errors, the ListBox is empty
        If results.Errors.Count = 0 Then
            Return Nothing
        Else
            'If any errors, creates a list of errors...
            Dim errorsList As New List(Of String)

            '..iterating the compiler errors
            For Each item As CompilerError In results.Errors
                errorsList.Add(item.ErrorText & " Line " & item.Line.ToString)
            Next
            Return errorsList.AsEnumerable
            errorsList = Nothing
        End If

    End Function
End Class

					  

At this point you can test the new tool window. This can be accomplished by simply pressing F5 as you would do in any other kind of .NET application. This starts a new instance of Visual Studio known as Experimental Hive. It is a fully functional instance of Visual Studio that is used for debugging custom extensions. If the new tool window is not visible, simply click the new View, Other Windows, Snippet Compiler command. Figure 7 shows how the new tool window appears in the IDE.

Figure 7. The new tool window running inside Visual Studio 2010.

The new tool window is a fully functional one, so it can be anchored like any other Visual Studio window. To stop the test environment, simply close the experimental instance of Visual Studio. Until now you saw a debugging scenario. When the debugging and testing phase is completed, you need to deploy the extension to other developers. As explained in the next section, Visual Studio 2010 offers a new, simple deployment system for this kind of extensions.

Other  
  •  Advanced FAQ Corner, May 2012
  •  The drive toward DSLs : Taking a DSL apart—what makes it tick?
  •  The drive toward DSLs : Choosing between imperative and declarative DSLs
  •  Visual Studio Team System 2008 : Creating new report (part 2)
  •  Visual Studio Team System 2008 : Creating new report (part 1) - Report server project
  •  Visual Studio Team System 2008 : TFS reports for testing - Bugs
  •  Extra Network Hardware Round-Up (Part 3)
  •  Extra Network Hardware Round-Up (Part 2) - NAS Drives, Media Center Extenders & Games Consoles
  •  Extra Network Hardware Round-Up (Part 1)
  •  Networking Jargon Explained (Part 2)
  •  Networking Jargon Explained (Part 1)
  •  The Micro Revolution
  •  Computing Yourself Fit (Part 4)
  •  Computing Yourself Fit (Part 3)
  •  Computing Yourself Fit (Part 2)
  •  Computing Yourself Fit (Part 1)
  •  Touch Interaction - Multi-Touch: An Evolution
  •  Think the Brighter Side to Piracy
  •  These Companies Would Still Be Here In 5 Years
  •  Build Up Your Dream House with PC (Part 4)
  •  
    Top 10
    Intel SSD 335 Series - Better Under The Hood
    Upgrade Your Mice & Keyboards – May 2013
    Printer Upkeep: Inkjet Printer Maintenance Tips
    Printers: Inkjet vs. Laser, And More
    WD Black 4TB - 4TB Storage Goes Mainstream
    Smart Phones: Bigger, Faster, And Better Than Ever
    Choice Exotica Well Tempered Versalex Turntable (Part 2)
    Choice Exotica Well Tempered Versalex Turntable (Part 1)
    Pre/ Power Amplifier - Nagra Jazz/ MSA (Part 2)
    Pre/ Power Amplifier - Nagra Jazz/ MSA (Part 1)
    Most View
    Choosing The Right Components (Part 3)
    Year End 2012 - Reviews & Rankings (Part 5)
    LaCie Little Big Thunderbolt Series SSD 240GB
    Windows Server 2003 : Active Directory - Understanding Operations Master Roles
    The Best Apps and Gear of 2012 (Part 3)
    SharePoint 2010 : Cataloging the Best Scripts to Automate SharePoint Administration
    Windows 8 vs. OS X Mountain Lion (Part 2)
    Game Changer - iPhone Revolution (Part 2)
    Apple's Undiscovered Country : Apple TV
    Transcend's aXe RAM Does The Trick
    iPhone Application Development : Working with Text, Keyboards, and Buttons (part 1) - Adding Text Fields
    iPhone 5 Greets The World (Part 2)
    SpeedUpMyPC 2012
    Deploying the Client for Microsoft Exchange Server 2010 : Deploying with Microsoft System Center Configuration Manager 2007
    iPad SDK : Popovers - The Font Name Popover (part 2)
    Science! – Spintronics
    Samsung 830
    For People On The Move
    Display Warriors (part 4) - AOC 2352Phz & Philips 226V3LSB28
    Silverlight Recipes : Using Sockets to Communicate over TCP (part 3)