Articles for .NET developers

Visual Studio Toolbox Control Integration

The Most Complete Guide to Visual Studio Toolbox Control Integration

Libor Tinka, Lead Developer, ComponentOwl.com

Contents

1. Introduction
2. Prerequisites
3. Creating a Sample Control
4. Manual Toolbox Integration
5. Toolbox Integration using TCI
6. Toolbox Integration using DTE
7. Toolbox Integration using VSI Packages
8. Toolbox Integration using VSPackages
9. Toolbox Integration using VSIX Packages
10. Supporting Multiple Version of .NET Framework
11. Sample Source Code

1. Introduction

This tutorial is intended for developers who would like to distribute their WPF or WinForms controls and automatically put them into Visual Studio Toolbox during installation.

I struggled with Toolbox integration earlier because there are several possible approaches (harder to decide between them). Each approach have its own pros and cons and no overall comparison is provided. I wrote this tutorial to shed some light on the topic and spare you hours, maybe days of research and experimenting with aspects of Visual Studio (Toolbox) extensibility.

We will first take a look on Toolbox control integration in general to get a big picture. Each approach will be then discussed in detail and the following question will be answered:

  • How to install control in Visual Studio Toolbox?
  • How to update the control?
  • How to uninstall/remove the control?
  • How to support multiple Visual Studio versions?

There are several options on how to integrate your controls with Visual Studio Toolbox:

  • Manual installation
  • Toolbox Control Installer (TCI)
  • Visual Studio Automation Object Model (DTE)
  • VSPackage
  • VSI package
  • VSIX package

Manual installation

The simplest way of adding control into Visual Studio Toolbox is from within the IDE.

This approach have one crucial drawback, which is that you leave Toolbox integration to the user. Many developers are not that experienced with Visual Studio and when your component is shipped, even if you provide appropriate step-by-step guide, they may find it too complicated and rather try another component which "just works". I thought that every developer using Visual Studio is experienced enough to know how to add new items in VS Toolbox, but I received few e-mails from users who uninstalled the product just because the component have not appeared in the Toolbox and they thought it is broken (without reading our documentation, of course). On the other hand, there is a group of users who are not experienced developers, but are in charge of trying some products in a given company (e.g. project managers). These people can install the component, play with it and they would really appreciate if it just works. This increases chance they will actually purchase your product.

Advantages: zero effort
Disadvantages: require experienced users, slows user producitivity, updating controls is not intuitive

Toolbox Control Installer (TCI)

Visual Studio 2005 SDK contained a VSPackage called Toolbox Control Installer. This package comes pre-installed with Visual Studio 2008 and newer. Its job is to simplify the specific task of extending Visual Studio Toolbox. This approach requires you to install your assembly in GAC (Global Assembly Cache) and create a key in Windows Registry.

Advantages: simple and fast component installation, updating and removing
Disadvantages: requires installation in GAC (not always wanted), VS 2005 supported with SDK only

Visual Studio Automation Object Model (DTE)

If you are not afraid of COM, you can try DTE (Development Tools Environment) approach. There is already a project on CodePlex called Visual Studio Toolbox Manager, which solves the toolbox integration problem using a simple command-line application. The project is outdated since it does not support Visual Studio 2010 and newer. I made a project called DteToolboxInstaller, which is also a command-line application and does support Visual Studio 2012, 2010, 2008 and 2005. You can use the project as you like. The main disadvantage of DTE approach is the speed. The installer have to run devenv.exe using the automation interface, create a fake VS Solution, open Toolbox, add the stuff and then close the Solution. The whole process take no less than 10 seconds. If you want to integrate with two or three versions of Visual Studio, it can take well over a minute.

Advantages: does not require updating registry or GAC, full control over Toolbox
Disadvantages: very slow, separate installation required for every version of Visual Studio

VSPackage

A VSPackage seems to be a natural option. VSPackages allow any type of Visual Studio extension and you can manipulate Toolbox as well. There was a trouble with VSPackages in providing a Package Load Key (PLK) which can be generated only manually using web form. The requirement for PLK vanished with Visual Studio 2010 (hooray!). The nice thing about VSPackage approcach is that it does not slow down the installation process. The package is loaded and the controls are installed on-demand (when the Toolbox is opened for the first time after installation). After trying all the approaches, using VSPackage seems to be fastest and most universal one.

Advantages: quick installation, appearance in About box and other extensibility features
Disadvantages: cmplicated setup, each component requires its own package if shipped separately

VSI Package

VSI packages are quite old but you can use them for integration with Visual Studio 2005 and newer. It have very simple structure and you can create one even without Visual Studio. The only trouble with VSI compared to other approaches is invoking a wizard form which cannot be suppressed. The installation just cannot run in "quiet" mode. Another trouble with VSI is that a digital signature is required in order to get rid of a warning dialog. Your control will be always installed under "My Controls" tab in the Toolbox, which is not always desirable.

Advantages: simple creation, installer provided by Visual Studio, automated creation and signing requires several specific steps
Disadvantages: no quiet mode (extra steps when custom installer is used), manual uninstallation

VSIX Package

VSIX packages came with Visual Studio 2010 so you can integrate with 2010 or newer. the .VSI and .VSIX file extensions are associated with Visual Studio so you can simply double-click it or run it via shell. You can also run VsixInstaller.exe utility that performs the installation. Good news: No more nag screens when VSIX is not signed - the installer only contains a dialog with simple text: "This extension does not contain a digital signature." Even better news: The VsixInstaller supports quiet mode!

Please note that VSI and VSIX package installers contain features like displaying EULA, choosing which components to install or localization. When deploying your controls for use in Visual Studio, you won't need an installer on top of the package.

Advantages: installer provided by Visual Studio, quiet mode, fast installation
Disadvantages: package project required, automated creation is complicated, no support for VS 2005 and 2008

Comparison of Approaches

Here is a table summarizing features of the discussed approaches. As you can see, the VSPackage approach gives you the most freedom, but is also hardest to implement. We will discuss every approach so that you will be able to impement the one that suits you best.

  2005 2008 2010 2012 Speed Install Automation Uninstall automation 
Manual installation depends on user
TCI fast  
EnvDTE slow  
VSI moderate  
VSIX moderate (faster than VSI)  
VSPackage fast  

2. Prerequisites

We will focus on integration with Visual Studio 2010 and 2012. Hence you will need:

  • Visual Studio 2010 (or 2012 or both)
  • Visual Studio 2010 SDK (or 2012 SDK or both)
  • Microsoft Windows SDK

The VS SDK contains regpkg.exe tool and project templates discussed in VSIX and VSPackage approaches.

The Windows SDK contains gacutil.exe, guidgen.exe, signtool.exe and other useful tools.

There are two kinds of versioning used for Visual Studio. One is based on the release name (e.g. Visual Studio 2008) and the other is a classic version number (e.g. 8.0). Both will be used, so it should be noted which version numbers correspond to which versions of Visual Studio:

Release name Version number
Visual Studio 2005 8.0
Visual Studio 2008 9.0
Visual Studio 2010 10.0
Visual Studio 2012 11.0

3. Creating a Sample Control

We will start by creating simple WinForms control for integration in VS Toolbox.

You can start with File - New - Project... (Control+Shift+N) and select Windows Forms Controls Library template.

Of course, you can also create empty Class Library project, add references to System.Drawing and System.Windows.Forms and create a new control. In fact, any DLL containing public classes derived from Control will suffice.

We want to support .NET Runtime version 4.0 and 4.5, so the control should be built against .NET 4.0 to ensure compatibility (the lower framework version you use, the wider range of compatible frameworks since they are backward compatible). It should be noted that .NET 4.5 is an in-place update of .NET 4.0 and hence the 4.5 assemblies will work on machines with 4.0 runtime installed unless you use some feature specific to 4.5.

If you have multiple controls in your assembly and don't want to use some of them in Toolbox, decorate them with ToolboxItem attribute with defaultType parameter set to false:

[ToolboxItem(false)]
public class InvisibleControl : UserControl
{
  ...
}

I have created a very simple control called SampleControl:

Finally, I set version of the assembly 3.3.0.0 (I chose just something else than 1.0.0.0 to see where the specific version number appears).

Custom Transparent Icon for the Toolbox

Icons for Toolbox are 16 by 16 pixel images. Various image formats are supported (BMP, JPEG, PNG and ICO). However, you need to create 256-color BMP image to ensure transparency. The transparent color is determined by bottom left pixel of the icon. Transparency works for magenta (#ff00ff):

The icon file should have same name as the control class (i.e. SampleControl.BMP).

Finally, use ToolboxBitmapAttribute to link icon with the control class:

[ToolboxBitmap(typeof(SampleControl), "Resources.SampleControl.bmp")]
public partial class SampleControl : UserControl
{
	...
}

Note that icon location matters, at least in C#. Since I have added the icon under custom folder named Resources, I need to reference Resources.SampleControl.bmp instead of just SampleControl.bmp.

Here is the resulting transparent icon in Toolbox:

Marking the Control as Toolbox Item

We can mark control as toolbox item by adding a ToolboxItemAttribute with defaultType parameter set to true:

[ToolboxItem(true)]
public partial class SampleControl : UserControl
...

This decoration is optional since the controls within assembly are considered toolbox items by default. However, we can mark certain control classes with ToolboxItem(false) to hide them from Toolbox. This comes in handy when we have multiple projects and there are too many controls in the Toolbox because loaded from all the other projects.

Signing the Assembly

The assembly containing controls (SampleControl.dll in our case) should be strongly named if we want them installed in GAC (Global Assembly Cache) later on. This is optional in most cases, but the Toolbox Controls Installer approach requires the assembly being installed in GAC, hence the strong name is necessary there.

To give an assembly a strong name, open project properties and find Signing tab:

Check the "Sign the assembly" option and select "<New...>" from the combo box. This will create a new .SNK file in your project which will be used to sign the assembly. You can also browse for existing key file. If you want to distribute multiple assemblies with custom controls, it is a best practice to use same strong name key for each assembly (it is possible to have one .SNK file located in Solution folder and put just a link to that file in each project; when we browse for the key under the Signing tab, the link will be used without copying the file).

The SNK (Strong Name Key) file is basically a private key to digitally sign your assembly. There is also a public key which can be used to verify the assembly and its shorter variant called "public key token" for assembly identification.

4. Manual Toolbox Integration

Installing

To install component into Visual Studio Toolbox manually, open some form or control in designer, open the Toolbox window (Control+Alt+X), right-click on the Toolbox window and select "Choose Items...":

The "Choose Toolbox Items" dialog will show up:

You can browse for DLL file with your component by clicking the "Browse..." button.

This is the simplest way of putting component in the Toolbox without extra actions required.

This can be unpleasant for end-users since it means many clicks they have to perform. I will explain how to integrate a component a little bit more so that it will be visible under the ".NET Framework Components" tab in the above dialog box and possibly show up in Toolbox automatically without extra effort of the user.

Making the Control Visible in "Choose Toolbox Items" Dialog Box

As you can see on the above picture, the SampleControl component is already displayed in the dialog box under ".NET Framework Components" tab.

This is because the folder containing our control is registered as "assembly folder" in the registry and hence is searched when the above dialog is populated.

You can register your own assembly this way by creating a key in registry:

32-bit OS: HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\<your control name>
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AssemblyFolders\<your control name>

You can also create key for specific version of .NET runtime (this comes in handy if you distribute different components for different versions of .NET):

32-bit OS: HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\<your control name>
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\<your control name>

In both cases, the default value for the key is a string with full path to the folder with your assembly.

You can specify Toolbox tab in which the component should show up by adding subkey named "Toolbox" with single string value "TabName" this value has Toolbox tab name as data. When you add such control in the Toolbox, it will reside under new tab with the specified name:

The control should also appear in "Choose Toolbox Items" dialog box if it is installed in Global Assembly Cache.

The control pops up in the Toolbox automatically in its own tab in Visual Studio 2012.

Installing the Control in GAC

The benefit of GAC (Global Assembly Cache) is that the user needs not to browse for your control. He will just select it form the above dialog box without having to know where it is actually installed (the dialog is populated by controls from "assembly folders" and from the GAC).

The GAC have one useful feature and disadvantage at the same time: It allows holding multiple versions of the same assembly. When user makes reference to your control from GAC and set "Specific Version" to true in Reference Properties window, it will be tied to that version. When you install an "update", a new version will be added to GAC, but the user will stay with the older one. Of course, the "Choose Toolbox Items" dialog will show both versions, so the user can just replace old reference with the new one.

You can make the installer removing any older versions from GAC during installation and add/keep just the newest one. This will force the user to replace the reference since it breaks the build.

You can work with GAC by using tool called gacutil.exe or from code. We will discuss both approaches.

The gacutil.exe is located in Microsoft Windows SDK directory. There are two such extecutables:

c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\gacutil.exe
c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\gacutil.exe

The former is for .NET Framework up to version 3.5. The latter is for .NET 4.0 and higher. This is for compatibility reasons as a separate GAC have been introduced with .NET 4.0.

You can install an assembly to GAC by calling:

gacutil.exe /i SampleControl.dll

To uninstall it, we refer to our assembly by its assembly name, not file name:

gacutil.exe /u SampleControl

Finally, you can check if the assembly is installed in GAC by listing any instances of the provided name:

gacutil.exe /l SampleControl

It is not wise, however, to use gacutil.exe from a custom installer as it is located in SDK that user might not have installed. Furthermore, the SDK license does not allow bundling gacutil.exe with your installer.

Some installers like Inno Setup or MSI allow installing in GAC anyway.

You can also work with GAC using System.EnterpriseServices.Internal.Publish class. The class have two methods: GacInstall and GacRemove. Both methods take just path to assembly file as a parameter, so for example:

(new Publish()).GacInstall(assemblyPath);

will install the specified assembly in GAC.

Updating

Updating the control depends on how it is installed and referenced.

If you have added component in the Toolbox manually via "Choose Toolbox Items" dialog box and "Browse..." button, i.e. as a file reference, the default property of such reference is that it simply points to the specified file no matter which version it have (unless user sets "Specific Version" to true in reference properties window; the default is false in this case). Simply replacing the DLL with the control by a newer file will suffice. If the user have specified "Specific Version" to true, the build will break because the reference is no longer valid. He needs to replace the reference by a new one pointing on the same file which now have newer version.

If you have added the component from GAC (these components also appear in the "Choose Toolbox Items" dialog box), the "Specific Version" property of the reference is true by default:

This means that even if you install a newer version of the component in GAC, the project will still reference the older version and both versions will reside in GAC.

If you remove all versions of the component from GAC (e.g. using gacutil.exe) and then install just the newest one, the build will break unless the user changed "Specific Version" property to false.

Removing

Removing the manually installed control consists of just reverting all the steps done during the installation.

In case of file references, deleting the file is sufficient.

In case of tighter integration (GAC, registry), the registry keys need to be deleted and the control can be removed from GAC (e.g. using gacutil.exe).

Resetting Toolbox and Clearing the Toolbox Cache

The Toolbox can fall into state where it does not display some items, some are duplicate and some can be disabled. Sometimes the only remedy is to let Visual Studio rebuild the Toolbox from scratch.

To do that, right-click on the Toolbox window and select "Reset Toolbox". Visual Studio will go through all the installed packages and reloads components into the Toolbox.

If this won't help, you can perform hard reset of the Toolbox. Exit Visual Studio and delete all .TBD files in the following folder:

\Users\<user>\AppData\Local\Microsoft\VisualStudio\10.0\

It should be up to four files:

Once removed, start Visual Studio again. After showing the Toolbox, all items should load instead of loading only the cached versions.

5. Toolbox Integration using TCI

Installing

Toolbox Control Installer is a VS package pre-installed in Visual Studio 2008 and newer. It looks in Windows registry for components and loads them in the Toolbox.

Before using TCI, one can check if it is installed in the given version of VS. For example, the following registry key should exist if the Visual Studio 2010 have TCI installed:

32-bit OS: HKLM\SOFTWARE\Microsoft\VisualStudio\10.0\Packages\{2c298b35-07da-45f1-96a3-be55d91c8d7a}
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\Packages\{2c298b35-07da-45f1-96a3-be55d91c8d7a}

The TCI package GUID is always the same so you can make the check for any version of Visual Studio with the above key (only change the version number from 10.0 to corresponding version number, of course).

The only prerequisites for the assembly is that it should have strong name (i.e. to be signed). See section "Creating the Sample Control" for more information.

The installation consists of putting the control in GAC (see previous section for more information) and creating registry keys.

Suppose we have the SampleControl installed in GAC:

We will make reference to this assembly from registry by creating the following key:

32-bit OS: HKLM\SOFTWARE\Microsoft\VisualStudio\10.0\ToolboxControlsInstaller\SampleControl, Version=3.7.0.0, Culture=neutral, PublicKeyToken=3cc4c7b61201d46c
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\ToolboxControlsInstaller\SampleControl, Version=3.7.0.0, Culture=neutral, PublicKeyToken=3cc4c7b61201d46c

The default value for the key is the Toolbox tab name name you would like to have for the component(s), e.g. "Component Owl".

Installing in Visual Studio 2012

One extra step is required to make this work in Visual Studio 2012, which is adding the registry key also in its user config hive, i.e.:

HKCU\Software\Microsoft\VisualStudio\11.0_Config\ToolboxControlsInstaller\SampleControl, Version=3.7.0.0, Culture=neutral, PublicKeyToken=3cc4c7b61201d46c

Updating

Updating the component is very simple. Just modify the above registry keys by changing the version number.

Removing

To remove the component, delete the above registry keys. You should also remove the corresponding assembly from GAC.

Automating Integration with TCI using TciToolboxInstaller

I made a simple command-line application called TciToolboxInstaller which does all the described steps. The usage is simple:

TciToolboxInstaller.exe [install|uninstall] [vs2005|vs2008|vs2010|vs2012] [tab name] [assembly path]

For example, if you like to install SampleControl.dll in Visual Studio 2012 Toolbox, just call:

TciToolboxInstaller.exe install vs2012 "Component Owl" SampleControl.dll

You can use quotes for the last two parameters if they contain spaces.

The TciToolboxInstaller project is contained in sample source code.

6. Toolbox Integration using DTE

Installing

The DTE (Development Tools Environment) approach does not require working with GAC or registry. It remotely manipulates Visual Studio Toolbox and adds/removes items as needed.

The whole installation is done from (managed) code using COM wrappers. It works in the following steps:

  • Check if an instance of Visual Studio is not running. If not, continue.
  • Retrieve an EnvDTE.DTE object corresponding to the version of Visual Studio we want to integrate with.
  • Create a "dummy" project using the DTE object
  • Obtain Toolbox window and ToolBox object from it.
  • Find or create ToolBoxTab object.
  • Add item in the Toolbox tab (ToolBoxTab.ToolBoxItems.Add).
  • Wait until current instance of Visual Studio stops running.

Here are some of the step/strongs in C# code - it is an excerpt from DteToolboxInstaller project provided in sample source code:

// obtain a DTE object
Type typeDTE = Type.GetTypeFromProgID("VisualStudio.DTE.11.0");

DTE dte = (DTE)Activator.CreateInstance(typeDTE, true);

// create a temporary file
string tempFile = Path.GetFileNameWithoutExtension(Path.GetTempFileName());
string tempDirectory = string.Format("{0}{1}", Path.GetTempPath(), tempFile);

// create Visual Studio Solution
Solution4 solution = (dte.Solution as Solution4);

string templatePath = solution.GetProjectTemplate(TemplateName, "CSharp");

solution.AddFromTemplate(templatePath, tempDirectory, DummyProjectName, false);

// get Toolbox window
Window window = dte.Windows.Item(Constants.vsWindowKindToolbox);

// get Toolbox
ToolBox toolBox = (ToolBox)window.Object;

// get Toolbox tab
ToolBoxTab toolBoxTab = (GetToolBoxTab(toolBox.ToolBoxTabs) ?? toolBox.ToolBoxTabs.Add(this.tabName));

// add new item under the Toolbox tab
toolBoxTab.ToolBoxItems.Add(assemblyName, this.assemblyPath, vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent);

// select the Toolbox tab
toolBoxTab.Activate();

// cleanup
dte.Solution.Close(false);
dte.Quit();
Marshal.ReleaseComObject(dte);

// wait till Visual Studio turns off completely
if (IsVisualStudioRunning())
{
	Thread.Sleep(VisualStudioProcessTimeout);
}

There are several obstacles on implementing the DTE approach.

First of all, we need to ensure that Visual Studio is not running during the installation - this is because we want messages sent to Visual Studio instance will arrive in the "invisible" one ran from our code and not the one which the user have currently opened.

Similarly, we would like to wait a while until the instance terminates after installation. This is necessary when integrating with multiple versions of Visual Studio when just a single instance have to be running at a time. Doing two installations too quickly in succession may cause the previous one to fail because a Visual Studio instance is still running.

The communication between our code and Visual Studio is mediated by OLE message filter which needs to be implemented. You can take a look on DteToolboxInstaller (see below) source code, where is a working installer implemented that uses this approach.

Updating and Removing

Since we have full control over the Toolbox with this approach, updating or removing items/tabs is done with the corresponding DTE objects.

Automatic Integration with DTE using DteToolboxInstaller

I made a simple command-line application called DteToolboxInstaller which does all the necessary steps and solves the deals with the discussed obstacles. The usage is simple:

DteToolboxInstaller.exe [install|uninstall] [vs2005|vs2008|vs2010|vs2012] [tab name] [assembly path]

For example, if you like to install SampleControl.dll in Visual Studio 2012 Toolbox, just call:

DteToolboxInstaller.exe install vs2012 "Component Owl" SampleControl.dll

You can use quotes for the last two parameters if they contain spaces.

The DteToolboxInstaller project is contained in sample source code.

7. Toolbox Integration using VSI Packages

Let's consider you don't have a custom installer and want to distribute your components in some kind of simple extension package that Visual Studio understands.

Visual Studio contains an installer for so called VSI packages that will do the integration work for you. If you have Visual Studio installed, the .VSI extension is already associated with the Visual Studio Content Installer.

Creating the VSI Package

I have created an empty folder and copied SampleControl.dll in it. All that is needed to make a VSI package is to create a .VSCONTENT file, which is simply a XML file satisfying Visual Studio Content Installer schema:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005"> 
    <Content>
        <FileName>SampleControl.dll</FileName>
        <DisplayName>SampleControl</DisplayName>
        <Description>ComponentOwl.com SampleControl</Description>
        <FileContentType>Toolbox Control</FileContentType>
        <ContentVersion>2.0</ContentVersion>
    </Content>
</VSContent>

The content is readable and pretty straightforward. The ContentVersion element can contain either "1.0" (support for Visual Studio 2005, 2008 and 2010) or "2.0" (support for Visual Studio 2008, 2010, 2012).

Now we zip the two files and rename extension of the archive to .VSI. We should end up with the following three files:

If you double-click the SampleControl.vsi, the Visual Studio Content Installer opens up. You can start the installer from command line as well:

32-bit OS: C:\Program Files\Common Files\Microsoft Shared\MSEnv\VSContentInstaller.exe SampleControl.vsi
64-bit OS: C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\VSContentInstaller.exe SampleControl.vsi

The installer have a form o wizard:

Signing the VSI Package

By default, the VSI package is not signed. This causes showing: "Publisher: Unknown" label on the first page of the installation wizard and an unpleasant dialog box later on:

To avoid this, you need to digitally sign the VSI package. Of course, you have to own a digital certificate (usually an X.509 certificate stored in .PFX file).

Because we cannot sign ZIP files, we need to convert the .VSI file (which is actually a ZIP archive with just an altered extension) to self-extracting archive that the Visual Studio Content Installer recognizes. There is a tool called MakeZipExe to do this task:

MakeZipExe.exe -zipfile:SampleControl.vsi -output:SampleControl-unsigned.vsi -overwrite

The MakeZipExe tool is located at Visual Studio's binary folder:

c:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\MakeZipExe.exe

The second step is signing the .EXE file using signtool.exe. You can find signtool.exe in Microsoft Windows SDK, i.e.:

c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe

Here is a sample usage of signtool.exe:

signtool.exe sign /du "http://www.componentowl.com/sample-control" /f certificate.pfx /p abc123 /t "http://timestamp.comodoca.com/authenticode" "SampleControl-signed.vsi"

When signed, the "Publisher" and "Information URL" labels are filled in the installer and no dialog box appears. The benefit of signing is obvious - your users will know they are installing trusted, possibly high-quality software and there is a higher chance of your software being used more widely (for example, the government sector usually require such certified software).

You can also add an EULA to your VSI package. This can be done by adding a comment metadata in the archive by ZIP archiver that supports such feature (e.g. WinZip).

Uninstalling the Control from Toolbox

As far as I know, the control cannot be removed from the Toolbox programmatically. You need to delete the control's DLL located at

c:\Users\<user>\Documents\Visual Studio 2010\Controls\

the same should be done for every version of Visual Studio installed.

In Visual Studio, right-click on Toolbox and select "Reset Toolbox". The component will not be found and disappear. Similarly, you can just delete the control from the Toolbox or select "Choose Items..." from context menu and untick the control there:

 

Drawbacks of Using VSI Package

One drawback of using VSI package is that the installer runs every version of VS IDE you have installed and which is supported by the package. The form will disappear eventually, but it lowers user experience.

Another drawback is that when you want to update your control, the installer offers whether to rename, replace or skip the file (e.g. SampleControl.dll). User have to decide to update, which also slows down installation and requires user interaction.

You also cannot specify custom Toolbox tab. All controls are installed under "My Controls" tab:

8. Toolbox Integration using VSPackages

This approach brings full control over the integration and other benefits. The VSPackages are loaded on-demand, so the integration process won't slow down a custom installer.

Although VSPackage and our sample control can be packed within the same assembly, we will create a separate Visual Studio Package project.

When user opens Toolbox in VS for the first time after installation, the IDE will look in registry for any registered packages and load them (if not loaded previously).

Creating VSPackage Project

We would like to have our VSPackage compatible with both Visual Studio 2010 and 2012, so we will work in Visual Studio 2010.

Select "File - New - Project.." (Control+Shift+N) and select the "Visual Studio Package" template:

This will start a "Visual Studio Package Wizard":

You can leave most options in the wizard on defaults. Leave all the check boxes unchecked on "Page 3 of 7" and "Page 7 of 7":

Now we will take a look on the generated files. Open the Guids.cs file:

// Guids.cs
// MUST match guids.h
using System;

namespace ComponentOwl.ToolboxIntegration
{
    static class GuidList
    {
        public const string GuidSampleVSPackagePkgString = "00000000-8fdf-48b6-98f8-4ff21a3a4def";
        public const string GuidSampleVSPackageCmdSetString = "def6519d-5ace-4062-95d6-4ee43f4a5de9";

        public static readonly Guid GuidSampleVSPackageCmdSet = new Guid(GuidSampleVSPackageCmdSetString);
    };
}

Here are the GUIDs that uniquely identify your package. I have edited the first four hex digits of package identifier to "00000000" so that we can find it more easily later. This is just for purpose of convenience in our sample project. Always use randomly generated GUID in a real-world application. Visual Studio will generate a new GUID whenever you create a new VSPackage project.

You can also generate new GUIDs any time, for example using online GUID generator or guidgen.exe utility from Windows SDK. When these numbers are changed, your package will be different from Visual Studio's point of view.

Another important file here is source.extension.vsixmanifest. If you double-click on the file in Solution Explorer, the VSIX Manifest Designer will show up:

Not all the fields are mandatory, but I will fill all of them nevertheless:

  • ID - Unique product "Identity" - the ID is limited to 100 characters and the recommended format is "Company.Product.Feature.Name". We can leave the VSPackage's GUID here.
  • Product Name - This field is used for Toolbox Tab name, so I will put "Component Owl Controls" here.
  • Author - Your name or company name - "ComponentOwl.com", for example.
  • Version - This is version of the package and its contents. The format is same as for assembly versions: Major.Minor.Build.Revision. I will put "1.4.0.128" here.
  • Description - Speaks for itself.
  • Locale - Language for the package.
  • Supported VS Editions - Here you can specify which editions of Visual Studio 2010 you would like to support. Of course, it can support VS 2012 as well, but for now I will just check Ultimate, Premium and Professional editions.
  • Supported Framework Runtime - Minimum and maximum .NET Framework Runtime versions your extension supports. Since my component will support 4.0 and 4.5 runtime, I will put 4.0 and 4.5 here.

Adding Support for Visual Studio 2012

We have specified supported Visual Studio Editions in VSIX Manifest Designer, through the "Visual Studio Version and Edition" dialog box:

As you can see, only Visual Studio 2010 is supported here because VSIX is new to 2010 and of course VS 2010 does not know about 2012. We have to source.extensions.vsixmanifest file for manual editing. Select the file in Solution Explorer and press F7 (View Code):

<?xml version="1.0" encoding="utf-8"?>
<Vsix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
  <Identifier Id="aaaaaaaa-8fdf-48b6-98f8-4ff21a3a4def">
    <Name>SampleVsPackage</Name>
    <Author>ComponentOwl.com</Author>
    <Version>1.0</Version>
    <Description xml:space="preserve">Information about my package</Description>
    <Locale>1033</Locale>
    <InstalledByMsi>false</InstalledByMsi>
    <SupportedProducts>
      <VisualStudio Version="10.0">
        <Edition>Ultimate</Edition>
        <Edition>Premium</Edition>
        <Edition>Pro</Edition>
      </VisualStudio>
    </SupportedProducts>
    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.5" />
  </Identifier>
  <References>
    <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
      <Name>Visual Studio MPF</Name>
    </Reference>
  </References>
  <Content>
    <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
  </Content>
</Vsix>

Take a look on the Vsix/Identifier/SupportedProducts/VisualStudio element (highlighted in bold). Copy and paste this element and modify Version attribute on the second one to "11.0":

<VisualStudio Version="11.0">
  <Edition>Ultimate</Edition>
  <Edition>Premium</Edition>
  <Edition>Pro</Edition>
</VisualStudio>

The edition tags are valid for version 11.0 because Visual Studio 2012 template generates the same edition names.

Writing Package Code

Now we will take a look on the VSPackage code itself. Open the SampleVSPackage.cs file. I kept only the necessary code and added the ProvideToolboxItems attribute:

[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[Guid(GuidList.guidSampleVsPackagePkgString)]
[ProvideToolboxItems(1)]
public sealed class SampleVsPackage : Package
{
}

Our VSPackage implementation inherits from Microsoft.VisualStudio.Shell.Package class and is decorated by three attributes:

  • PackageRegistrationAttribute - Specifies that package registration tool should look for additional attributes (will be discussed later).
  • InstalledProductRegistrationAttribute - Provides information for the Visual Studio splash screen and About box.
  • GuidAttribute - Provides custom GUID for the class because automatic GUID is undesirable here (Visual Studio need to be able to trace our package by its unique ID).
  • ProvideToolboxItemsAttribute - Specifies that the package provides toolbox items. There are various uses of VSPackages, but we are interested in intalling controls to Visual Studio Toolbox, hence this attribute.

The strings "#110" and "#112" in InstalledProductRegistrationAttribute refer to keys in VSPackage.resx. You can open this file and edit package name and description there:

Now we write methods within SampleVsPackage class that work with the Toolbox:

private const string ComponentFile = "SampleControl.dll";
private const string TabName = "Component Owl";

private void InstallToolboxItems()
{
    IToolboxService toolboxService = (IToolboxService)GetService(typeof(IToolboxService));

    foreach (ToolboxItem item in ToolboxService.GetToolboxItems(GetAssemblyName()))
    {
        toolboxService.AddToolboxItem(item, TabName);
    }
}

private void RemoveToolboxItems()
{
    IToolboxService toolboxService = (IToolboxService)GetService(typeof(IToolboxService));

    foreach (ToolboxItem item in ToolboxService.GetToolboxItems(GetAssemblyName()))
    {
        toolboxService.RemoveToolboxItem(item);
    }
}

private AssemblyName GetAssemblyName()
{
    string pathAssembly = String.Concat(
        Path.GetDirectoryName(GetType().Assembly.Location),
        Path.DirectorySeparatorChar,
        ComponentFile);

    return AssemblyName.GetAssemblyName(pathAssembly);
}

The method names InstallToolboxItems and RemoveToolboxItems speak for themselves. Both methods look for SampleControl.dll in the same location as the VSPackage's assembly. They get all toolbox items from the assembly and either put them under "Component Owl" tab or remove them.

The ToolboxService class comes from System.Drawing.Design and we need to add reference to this asssembly in order to use ToolboxService.

Building the Package

Before building the SampleVsPackage project, open project properties, find the VSIX tab and uncheck all the options:

Finally, build the project. Just two files, SampleVsPackage.dll and SampleVsPackage.pdb, should be generated.

Registering the Package

Until the package can be loaded by Visual Studio, it needs to be registered. The registration is simply writing specific keys into Windows Registry.

To do that, find the Package Registration Utility (RegPkg.exe). It should be located in Visual Studio SDK directory, e.g.:

32-bit OS: c:\Program Files\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Tools\Bin\RegPkg.exe
64-bit OS: c:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Tools\Bin\RegPkg.exe

You can copy the tool where it suits you.

Here is a sample usage of RegPkg:

32-bit OS: RegPkg.exe /root:SOFTWARE\Microsoft\VisualStudio\11.0 /codebase SampleVsPackage.dll
64-bit OS: RegPkg.exe /root:SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0 /codebase SampleVsPackage.dll

This will write package registration information into the Windows Registry, hence registers the package. Similar call have to be done by your custom installer in order to register the package.

Instead of writing into registry, RegPkg.exe can gereate a REG file (several other formats are available) so that you can write package information into registry using the file. To do that, use /regfile parameter:

32-bit OS: RegPkg.exe /root:SOFTWARE\Microsoft\VisualStudio\11.0 /regfile:SampleVsPackage.ref /codebase SampleVsPackage.dll
64-bit OS: RegPkg.exe /root:SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0 /regfile:SampleVsPackage.ref /codebase SampleVsPackage.dll

This creates SampleVsPackge.reg file you can use any time later instead of RegPkg.exe itself.

There are two other options for specifying how the package will be registered: codebase and assembly. When /codebase parameter is used (as in the sample above), the registry will point to the location on disk where your package is located (see Assembly.CodeBase property for more information).

Another option is the /assembly parameter - this assumbes that your VSPackage assembly is located in GAC (Global Assembly Cache). See section Installing the Control in GAC for more information.

You can check out the registry after the package registration:

Package Registration for Visual Studio 2012

Regrettably, simply registering package is not enough for Visual Studio 2012 to load it (see this blog post). Because of performance optimizations, VS developers removed feature that looks for changes in VS registry root and thus we need to call

devenv.exe /Setup

In order to finish package registration.

This call can be very time consuming since Visual Studio 2012 goes through all extensions and looks for changes. On the other hand, I tried running devenv.exe with /Setup parameter on fresh install of Visual Studio 2012 and it was instant. On older installation, however, the operation took well over a minute (it behaves just like Microsoft Windows, which progressively slows down during its lifetime).

We can speed things up by a little hack. One of the things the /Setup does is copying registry keys from HKLM to Visual Studio's 11.0_Config hive. We can just write registry under this key instead of calling devenv.exe and avoid possibly lengthy operation.

The hive is located in HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config.

So let's open and edit the SampleVsPackage.reg file we have generated using RegPkg.exe earlier. Here is the modified version where only the registry root has been changed:

REGEDIT4

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config\InstalledProducts\SampleVsPackage]
@="#110"
"Package"="{00000000-8fdf-48b6-98f8-4ff21a3a4def}"
"PID"="1.0"
"ProductDetails"="#112"
"LogoID"="#400"
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config\Packages\{00000000-8fdf-48b6-98f8-4ff21a3a4def}]
@="ComponentOwl.ToolboxIntegration.SampleVsPackage, SampleVsPackage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=30782fc44cbe0af5"
"InprocServer32"="C:\\Windows\\SYSTEM32\\MSCOREE.DLL"
"Class"="ComponentOwl.ToolboxIntegration.SampleVsPackage"
"CodeBase"="C:\\projects\\articles\\2012-10-22 Visual Studio Toolbox Control Integration\\ToolboxIntegration\\SampleVsPackage\\bin\\Debug\\SampleVsPackage.DLL"
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config\Packages\{00000000-8fdf-48b6-98f8-4ff21a3a4def}]
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config\Packages\{00000000-8fdf-48b6-98f8-4ff21a3a4def}\Toolbox]
"Default Items"=dword:00000001

The highlighted parts have been edited.

So in addition to standard package registration, we also write in the registry where Visual Studio 2012 user config hive resides. This is sufficient for our VSPackage to load.

However, this is really a hack - editing of user configuration in registry may cause Visual Studio to not load your user settings and show up as when running for the first time. I tried this hack on my machine and it worked, but there may be some hidden glitches. If you want to follow standard procedure, just call "devenv.exe /Setup".

Loading VSPackage

If you did all the previous steps, your package should load when you open Windows Forms Designer and show Toolbox (Cotrol+Alt+X). You can notice your package name displaying in status bar for a while, then SampleControl should show up in the Toolbox under "Component Owl" tab:

Displaying Your Extension in VS About Box

If you want information about your extension to be visible in Visual Studio splash screen and About Box, implement IVsInstalledProduct interface:

...

public sealed class SampleVsPackage : Package, IVsInstalledProduct
{

...

	int IVsInstalledProduct.IdBmpSplash(out uint pIdBmp)
	{
	    pIdBmp = 0;
	    return 0;
	}
	
	int IVsInstalledProduct.IdIcoLogoForAboutbox(out uint pIdIco)
	{
	    pIdIco = 400;
	    return 0;
	}
	
	int IVsInstalledProduct.OfficialName(out string pbstrName)
	{
	    pbstrName = "ComponentOwl SampleControl";
	    return 0;
	}
	
	int IVsInstalledProduct.ProductDetails(out string pbstrProductDetails)
	{
	    pbstrProductDetails = "SampleControl control.\r\nFor more information see http://www.componentowl.com";
	    return 0;
	}
	
	int IVsInstalledProduct.ProductID(out string pbstrPID)
	{
	    pbstrPID = "3.3.0.0";
	    return 0;
	}
	
	...
	
}

This code causes the component to show up in the list of "Installed Products" in Visual Studio about box:

As for the splash screen, Visual Studio 2008 used to display extensions in its splash screen, but 2010 and 2012 does not do so:

       

Troubleshooting Package Load Failures

You may encounter this dialog when playing with packages:

When you click "No", the package will be skipped later when loading packages. You can re-enable loading all packages by running

devenv.exe /ResetSkipPkgs

To debug package load problem, you can do just what the dialog says. Run

devenv.exe /log

and then take a look on the ActivityLog.xml (path is shown in the dialog). There you can find cause of the problem in one of the "entry" elements:

...
<entry>
  <record>106</record>
  <time>2012/10/26 06:07:36.920</time>
  <type>Error</type>
  <source>VisualStudio</source>
  <description>CreateInstance failed for package [ComponentOwl.ToolboxIntegration.SampleVSPackage, SampleVSPackage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=87379c2b0cde9bc3]</description>
  <guid>{761F0CB7-64C1-4695-91D2-6E3C26C12314}</guid>
  <hr>80070002</hr>
  <errorinfo>Could not load file or assembly 'file:///C:\projects\articles\2012-10-22 Visual Studio Toolbox Control Integration\ToolboxIntegration\SampleVSPackage\bin\SampleVSPackage.DLL' or one of its dependencies. The system cannot find the file specified.</errorinfo>
</entry>
...

In this particular case, the problem was caused by changing output path from "bin\SampleVSPackage.dll" to "bin\Debug\SampleVSPackage.dll" so the file does not longer exist at the location for which it is registered. The solution is to either change the location back or unregister the package (i.e. remove the corresponding registry entries - the GUID is provided in the log).

Past Troubles with Package Load Keys

The above problem with package load failure happened on Visual Studio 2005 and 2008 because a Package Load Key (PLK) had to be provided by the VSPackage. The PLK is basically a hashcode computed from metadata about package (name, author/company, version). The PLK had to be obtained from a website provided by Microsoft.

I believe PLK caused many troubles and headaches to developers, including myself.

This is no longer relevant for Visual Studio 2010 and newer (requirement for PLKs removed), so we won't discuss this topic in more depth

Update Control Already Installed in Toolbox

Suppose we have already integrated SampleControl version 3.3.0.0 in the Visual Sudio Toolbox:

We would like to update this control to version 3.4.0.0.

First of all, we update assembly information of the SampleControl project:

If we "deploy" (copy) SampleControl.dll to the folder with SampleVsPackage.dll where it is registered, the SampleControl will no longer be visible in Toolbox, because the control in Toolbox should still be 3.3.0.0 and this version is no longer to be found.

You don't need to increment assembly version of the SampleVsPackage project, but at least you have to increment parameter of the ProvideToolboxItems attribute:

[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[Guid(GuidList.guidSampleVsPackagePkgString)]
[ProvideToolboxItems(2)]
public sealed class SampleVsPackage : Package, IVsInstalledProduct
{
	...
}

The package need to be re-registered (see section Registering the Package) which will effectively update just the "Default Items" value in the Toolbox key:

This will cause Visual Studio to update your control in the Toolbox:

Remove Control from the Toolbox

Now we would like to remove control from the Visual Studio Toolbox. This step can be done by custom uninstaller.

One way to do that is to simply unregister the VSPackage using RegPkg.exe:

32-bit OS: RegPkg.exe /unregister /root:SOFTWARE\Microsoft\VisualStudio\10.0 SampleVsPackage.dll
64-bit OS: RegPkg.exe /First of all, we update assembly information of the unregisFirst of all, we update assembly information of the ter /root:SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0 SampleVsPackage.dll

You can also do this manually by simply removing the registry entry of the corresponding package, e.g.:

32-bit OS: HKLM\SOFTWARE\Microsoft\VisualStudio\10.0\Packages\{a9696de6-e209-414d-bbec-a0506fb0e924}
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\Packages\{a9696de6-e209-414d-bbec-a0506fb0e924}

On Visual Studio 2012, the key need to be removed in user config registry hive or call "devenv.exe /Setup" after removing the registry key in HKLM. For more information, see section Package Registration for Visual Studio 2012.

9. Toolbox Integration using VSIX Packages

Microsoft have removed most /po f the drawbacks of VSI packages by introducing VSIX. The price is that VSIX is a little bit more complicated and Visual Studio 2008 is no longer supported.

There are two versions of VSIX Schema. Version 1.0 is what Visual Studio 2010 understand. There is also version 2.0 for Visual Studio 2012. We want a VSIX Package compatible with both, so we will work in Visual Studio 2010.

Create a new Project from Template

If you have Visual Studio 2010 SDK installed, you can create a new VSIX package project with control from a template.

Select "File - New - Project.." (Control+Shift+N) and then select the "Windows Forms Toolbox Control" or "WPF Toolbox Control" template:

The projects is basically a VSPackage wrapped in VSIX container after build. The package assembly also contain the control class named ToolboxControl.

There are three important files generated by the template:

  • ProvideToolboxControlAttribute.cs - This is attribute for ToolboxControl class. We will discuss it later.
  • source.extension.vsixmanifest - This is manifest XML file for our VSIX package. It contains all information about the package and what it contains.
  • ToolboxControl.cs - This is a sample control to be installed in Visual Studio Toolbox.

Create a new Project from VSPackage

We can also start with VSPackage like the one we have already created in previous section. I will create a new VSPackage project (as in previous chapter), name it SampleVsixPackage and configure it according to "Windows Forms Toolbox Control" template to show you all the differences.

The basic configuration of source.extension.vsixmanifest is the same as in previous chapter.

Project properties differs from VSPackage we have created earlier on the VSIX tab, where we have the first two check boxes checked:

Update the Manifest File

Double-click on the source.extension.vsixmanifest file to open up the VSIX Manifest Designer:

If you are not sure about some part of the form, please take a look on section Create VSPackage Project, where the form is described in more detail.

In addition to previous VSPackage project, I have also filled the following optional boxes:

  • License Terms - If you have EULA or other licence in TXT or RTF format, you can browse for it.
  • Icon - You can browse for an icon representing the extension. It should be 32x32 pixels large, PNG, BMP, JPEG or ICO image format.
  • Preview Image - Thumbnail image representing the extension. It should be 200x200 pixels large, PNG, BMP or JPEG image format.
  • More Info URL - URL of a website containing more information about the extension.
  • Getting Started Guide - URL of a website with documentation; you can also provide relative path to HTML file with the local documentation.

Add Control

Let's create a new WPF control within the SampleVsixPackage project itself. I will name it SampleWpfControl to distinguish it from SampleControl we have created earlier.

To ensure our control will show up in Toolbox of Visual Studio 2012, we have to decorate the SampleWpfControl class by ProvideToolboxControlAttribute:

[ProvideToolboxControl("SampleWpfControl", true)]
public partial class SampleWpfControl : UserControl
{
	...
}

You also have to provide implementation of the attribute class:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
[System.Runtime.InteropServices.ComVisibleAttribute(false)]
public sealed class ProvideToolboxControlAttribute : RegistrationAttribute
{
	private const string ToolboxControlsInstallerPath = "ToolboxControlsInstaller";

	public ProvideToolboxControlAttribute(string name, bool isWpfControls)
	{
		if (name == null)
		{
			throw new ArgumentException("name");
		}

		this.Name = name;
		this.IsWpfControls = isWpfControls;
	}

	private bool IsWpfControls { get; set; }
	private string Name { get; set; }

	public override void Register(RegistrationAttribute.RegistrationContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}

		using (Key key = context.CreateKey(String.Format(CultureInfo.InvariantCulture, "{0}\\{1}",
														 ToolboxControlsInstallerPath,
														 context.ComponentType.Assembly.FullName)))
		{
			key.SetValue(String.Empty, this.Name);
			key.SetValue("Codebase", context.CodeBase);
			if (this.IsWpfControls)
			{
				key.SetValue("WPFControls", "1");
			}
		}

	}

	public override void Unregister(RegistrationAttribute.RegistrationContext context)
	{
		if (context != null)
		{
			context.RemoveKey(String.Format(CultureInfo.InvariantCulture, "{0}\\{1}",
														 ToolboxControlsInstallerPath,
														 context.ComponentType.AssemblyQualifiedName));
		}
	}
}

This code is generated if you create project from template.

The project in Solution Explorer should look like this:

Adding Controls from Other Projects

What if we would like to use SampleControl.dll as in the VSI package scenario?

Of course, we can click "Add Content" in the VSIX Manifest designer and simply add "Toolbox Control" content from other project:

However, this is possible only if the SampleControl project itself is a package project!

Lucklily, since the VSIX package is still just a ZIP archive, we can take a look on how to add such external DLLs to it manually.

Setting Up VSIX Installer

The VSIX Installer tool (VsixInstaller.exe) is located in Visual Studio's binary folder:

32-bit OS: c:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\VSIXInstaller.exe
64-bit OS: c:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\VSIXInstaller.exe

You can hit F6 and Visual Studio will build your project to create SampleVsixPackage.vsix file. This is our VSIX package.

The VSIX Installer should be associated with the .VSIX file extension, so it is usually possible to just double-click on the file and see the VSIX installer.

The installation can fail in the very first step:

This problem appears if you have invalid manifest file. If this happen, open source.extension.vsixmanifest and fill in all missing data. Furthermore, you can check if the XML is valid according to VSIX Extension Schema.

Now we are able to rebuild and run the VSIX installer again:

Make the VSIX Package Compatible with Visual Studio 2012

To make our VSIX package working with Visual Studio 2012 and newer, we need to manually update the manifest file. Select the source.extension.vsixmanifest file and press F7 (View Code):

<?xml version="1.0" encoding="utf-8"?>
<Vsix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
  <Identifier Id="ComponentOwl.ToolboxControl.Express">
    <Name>Component Owl</Name>
    <Author>ComponentOwl.com</Author>
    <Version>1.0</Version>
    <Description xml:space="preserve">Windows Forms Toolbox Control</Description>
    <Locale>1033</Locale>
    <MoreInfoUrl>http://www.componentowl.com/toolbox-control</MoreInfoUrl>
    <License>license.rtf</License>
    <GettingStartedGuide>http://www.componentowl.com/documentation/toolbox-control</GettingStartedGuide>
    <Icon>icon.png</Icon>
    <PreviewImage>overview.jpg</PreviewImage>
    <SupportedProducts>
      <VisualStudio Version="10.0">
        <Edition>Ultimate</Edition>
        <Edition>Premium</Edition>
        <Edition>Pro</Edition>
      </VisualStudio>
      <VisualStudio Version="11.0">
        <Edition>Ultimate</Edition>
        <Edition>Premium</Edition>
        <Edition>Pro</Edition>
      </VisualStudio>
    </SupportedProducts>
    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="5.0" />
  </Identifier>
  <References />
  <Content>
    <ToolboxControl>|%CurrentProject%;PkgdefProjectOutputGroup|</ToolboxControl>
  </Content>
</Vsix>

The bolded text have been added. I have simply added a new VisualStudio element with higher version and all the editions (they are relevant for VS 2012 as its own template also generates these).

The VSIX installer will show Visual Studio 2012 options as well after this update (if installed, of course):

Please note that Visual Studio 2012 also works with 2.0 version of the schema, so if you create VSIX package in Visual Studio 2012, it won't be compatible with 2010. The solution is hence to use 1.0 version of the schema and add support for newer Visual Studio as described earlier.

A great advantage over VSI package is that installation of Toolbox control is really fast with VSIX.

Regrettably, the control won't show up in Visual Studio 2012 Toolbox in its default configuration. You need to enable loading-per user extensions (this option is enabled by default in Visual Studio 2010):

Signing the VSIX Package

Unlike older VSI package, there is no nag screen when the package is not signed. Instead, a label appears informing user that the package is not signed.

To sign a VSIX package, we need PackageSignatureManager from System.IO.Packaging (WindowsBase.dll). I made a simple command-line application called SignVsix (you can find it in sample source code) that takes three arguments (VSIX file path, PFX certificate path and password for the certificate):

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

internal class Program
{
	private static void Main(string[] args)
	{
		// first argument - path to VSIX package
		string paramPathPackage = args[0].Replace("\"", "");
		// second argument - path to PFX certificate
		string paramPathCertificate = args[1].Replace("\"", "");
		// third argument - password for the certificate
		string paramPassword = args[2];

		// open VSIX package
		Package package = Package.Open(paramPathPackage, FileMode.Open);

		// load certificate
		byte[] certificate = File.ReadAllBytes(paramPathCertificate);

		// sign all parts of the package
		var signatureManager = new PackageDigitalSignatureManager(package)
		{
			CertificateOption = CertificateEmbeddingOption.InSignaturePart
		};

		List<Uri> partsToSign = new List<Uri>();

		foreach (PackagePart packagePart in package.GetParts())
		{
			partsToSign.Add(packagePart.Uri);
		}

		partsToSign.Add(PackUriHelper.GetRelationshipPartUri(signatureManager.SignatureOrigin));
		partsToSign.Add(signatureManager.SignatureOrigin);
		partsToSign.Add(PackUriHelper.GetRelationshipPartUri(new Uri("/", UriKind.RelativeOrAbsolute)));

		try
		{
			signatureManager.Sign(partsToSign, new X509Certificate2(certificate, paramPassword));
		}
		catch (CryptographicException cryptographicException)
		{
			Console.WriteLine("Signing failed: {0}", cryptographicException.Message);
		}
	}
}

The usage is very simple:

SignVsix.exe SampleVsixPackage.vsix certificate.pfx abc123

When the file is signed, VSIX Installer shows label "Digital Signature: <Author Name>" on the first page:

Dissecting the VSIX Package

If you look on the project references, you can see reference to Microsoft.VisualStudio.Shell.Immutable.10. This reference points to Visual Studio 2010 SDK and we cannot expect this dependency present on end-user's machine. This library contains ProvideToolboxControlAttribute class, which is used by our ToolboxControl.

Since a software development company may want to develop many components, it would be nice to have an universal VSIX package which can be adjusted for any control.

Let's take a look on the ToolboxControl.vsix. It is simply a ZIP archive containing the manifest, resources, ToolboxControl binary and a ToolboxControl.pkgdef file. If we look through all its content, we easily generate our own VSIX packages on demand, even without Visual Studio. There should also be a programmatic way on generating VSIX packages using classes from System.IO.Packaging.

Update Toolbox Control via VSIX Package

If you made changes to your control and want to re-install the package, an error message appear:

In order to provide an update, you need to increment version number in the VSIX manifest:

You can also increment version number in the Package class attribute, but this is not necessary for the VSIX to perform update:

[InstalledProductRegistration("#110", "#112", "2.0", IconResourceID = 400)]
[Guid(GuidList.guidSampleVsixPackagePkgString)]
public sealed class SampleVsixPackage : Package
{
	...
}

Of course, you can also increment version of the assembly.

Uninstall the VSIX Package

The VSIX Installer can be used to uninstall control from the Toolbox via /uninstall parameter followed by package ID (the constant located in Guids.cs: GuidList.guidSampleVsixPackagePkgString):

VSIXInstaller.exe /uninstall:e3dfd099-d0ab-4b8e-b26d-639032c29ad9

It is also possible to uninstall VSIX Package manually using Extension Manager (Tools - Extension Manager...). In Visual Studio 2012, the corresponding dialog is called Extensions and Updates (Tools - Extensions and Updates...).

Quiet Mode

Both installation and uninstallation can be performed in quiet mode by using /quiet parameter. This will suppress user interface of the installer, which is handy when you want to automate Toolbox control integration with your custom installer.

10. Supporting Multiple Version of .NET Framework

Since .NET Framework is backward-compatible, building a component on lowest possible framework ensures compatibility with higher versions as well.

I heard from several users that the component may not be displayed in Toolbox although it seems that the Toolbox respects the .NET compatibility and display .NET 2.0 component even when working in .NET 4.0 (Client Profile) project.

There is also a scenario where you want to support additional features from higher version of .NET (for example, drawing text using GDI+ in .NET 2.0 and drawing text using WPF in .NET 3.5 and higher). You may also want to add extensive Windows Forms Designer support, which is not available in Client Profile framework.

The solution to this is to build several DLLs, each with different features and possibly different target frameworks. Then integrate all the assemblies.

This does not pose a problem when DTE approach is used, alhtough it is better to give each version of the component unique name or place them in separate tabs (e.g. "Component Owl WinForms - .NET 2.0").

When TCI approach is used, each version of the assembly requires different public key token, because they have to reside in GAC side-by-side. Furthermore, they need a separate registry key based on the public key token.

The VSI and VSIX approaches require renaming the component or customizing Toolbox tab in the ProvideToolboxControlAttribute (see part Toolbox Control Integration using VSIX Packages for more information).

The VSPackage approach allows you to place all the versions in Visual Studio Toolbox under their respective tabs.

When manual approach is used, you can of course add each version of the assembly separately and also create separate tabs in the Toolbox.

11. Sample Source Code

The attached sample source is a Visual Studio 2010 Solution containing implementations of all the presented approaches. The binaries are contained under "bin\Release" subfolders and batch files (.CMD extension) are provided where appropriate.

Download sample source code (212 KB)

You can find the following folders in the archive:

DteToolboxInstaller - A command-line application for installing/uninstalling assemblies in VS Toolbox using Visual Studio Automation Object Model (DTE). Custom tab name and VS version can be specified. The tool can be used in real-world application.

SampleControl - Windows Forms control for testing the integration.

SampleVsixPackage - VSIX package project (basically a VSPackage that is further packaged with the sample control), the resulting package can be installed by VSIX Installer that comes with Visual Studio.

SampleVsPackage - VSPackage that is able to install all control assemblies located in its own folder. Contains batch files for package registration/unregistration.

SignVsix - A command-line application for signing VSIX packages. Sample batch file is provided. Valid PFX certificate need to be provided by the user.

TciToolboxInstaller - A command-line application for installing/uninstalling assemblies in VS Toolbox using Toolbox Control Installer package (but does not depend on it). Custom tab name and VS version can be specified. The tool can be used in real-world application.

VSI - Basic setting for creating VSI package and a sample VSI package created from the files. Batch file for signing the VSI package is provided.

 

Download sample source code (212 KB)

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.Component Owl WinForms - .NET 2.0