Files
componentowl.com/articles/visual-studio-toolbox-control-integration.html

1838 lines
103 KiB
HTML
Raw Normal View History

2026-03-23 16:10:59 +00:00
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta name="keywords" content="list view, listview, list view control, list view component, .net list view, list view replacement, list view alternative, improved list view, enhanced list view" />
<meta name="description" content="Better ListView by Component Owl is a .NET WinForms control designed to replace the standard list view control included with C#/VB.net." />
<meta name="author" content="Dextronet" />
<meta http-equiv="Title" content="Visual Studio Toolbox Control Integration - visual studio" />
<meta name="copyright" content="(c) 2010-2018 Dextronet" />
<meta name="distribution" content="Global" />
<meta name="rating" content="General" />
<meta name="robots" content="All" />
<meta http-equiv="Content-Language" content="en" />
<meta name="verify-v1" content="NK0H1gWia1vxGZ2Yhr59gsS0/P2/USBI1DVA18VkzjM=" />
<meta name="google-site-verification" content="O7Dwtzu5x_Mob9u98uxqpZ-_wCLGpEkx2IL0UYVQ4ac" />
<title>Visual Studio Toolbox Control Integration - visual studio</title>
<link rel="alternate" type="application/rss+xml" title="Component Owl Feed: Latest releases, news and tips &amp; tricks from our blog" href="../feeds.rss" />
<link rel="alternate" type="application/rss+xml" title="Component Owl's Comics: Latest comic strips for developers by Libor Tinka" href="../comics.rss" />
<link href="../stylesheets/base_packaged.css%3F1455269822.css" media="screen" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="d-page">
<script type="text/javascript">
//<![CDATA[
var t = {"antispam":"capek-rulz","purchase_email":"valid purchase email","valid_email":"a valid email","thanks_for_why_uninstall":"Thank you very much for your feedback!","thank_you":"Thank you!","current_email":"valid current email","thanks_for_subscription":"<div class=\"sent-saved-notice\">Thank You for your subscription!<br /><button type=\"button\" onclick=\"close_fancybox();\">Close</button>","message":"message","required":"required","download_in_progress":"Your download should be in progress...","your_full_name":"your full name","order_thanks_for_message":"<div class=\"sent-saved-notice\">Thank you very much for your message!<br /><button type=\"button\" onclick=\"close_fancybox();\">Close</button>","thanks_for_message":"Thank you very much for your message!<br /><a href="http://www.componentowl.com/articles/\&quot;#\"" onclick=\"send_another(); return false;\">Send Another Message</a>"}
//]]>
</script>
<div class="d-header d-placing">
<ul class="d-menu">
<li class=""><a href="../index.html" class="menu-item">Home</a></li>
<li class=" with-dropdown">
<a href="../better-listview.html" class="menu-item">Products</a>
<div class="dropdown dropdown-submenu" style="display: none; width: 370px"><div class="outer"><div class="shadowbox"><div class="border"><div class="inner">
<div class="dropdown-category">WinForms Components</div>
<a href="../better-listview.html" class="featured-item"><img alt="icon" src="http://assets.componentowl.com/icons/better-listview-32-1355160256.png?1355135056" /> Better ListView<br /><span class="subline">Ultimate ListView control for .NET</span></a>
<a href="../better-thumbnail-browser.html" class="featured-item"><img alt="icon" src="http://assets.componentowl.com/icons/better-thumbnail-browser-32-1355160281.png?1355135081" /> Better Thumbnail Browser<br /><span class="subline">Thumbnail loading and browsing control for .NET</span></a>
<a href="../better-splitbutton.html"><img alt="icon" src="http://assets.componentowl.com/icons/better-splitbutton-32-1355160307.png?1355135107" /> Better SplitButton<br /><span class="subline">Free customizable dropdown button control</span></a>
<a href="../better-listview-express.html"><img alt="icon" src="http://assets.componentowl.com/icons/better-listview-express-32-1355160327.png?1355135127" /> Better ListView Express<br /><span class="subline">Free edition with less features</span></a>
</div></div></div></div></div>
</li>
<li class="">
<a href="../pricing-licensing/better-listview.html" class="menu-item">Purchase</a>
<div class="dropdown dropdown-submenu" style="display: none; width: 330px"><div class="outer"><div class="shadowbox"><div class="border"><div class="inner">
<a href="../pricing-licensing/better-listview.html" class="featured-item">Better ListView</a>
<a href="../pricing-licensing/better-thumbnail-browser.html" class="featured-item">Better Thumbnail Browser</a>
<a href="../pricing-licensing/better-splitbutton.html">Better SplitButton</a>
</div></div></div></div></div>
</li>
<li class=""><a href="../support.html" class="menu-item">Support</a></li>
<li class="active"><a href="../articles.html" class="menu-item">Articles</a></li>
<li class=""><a href="../blog.html" class="menu-item">Blog</a></li>
<li class=""><a href="../comics/43.html" class="menu-item">Comics</a></li>
<li class=""><a href="../about-us.html" class="menu-item">About Us</a></li>
</ul>
<div class="d-logo">
<a href="../index.html" class="logoimg"><img alt="Component Owl" src="../images/componentowl.gif%3F1455269978" /></a>
<g:plusone size="medium" count="false" href="http://www.componentowl.com"></g:plusone>
</div>
</div>
<div class="d-placing">
<div class="d-content-wrap">
<div class="subpage article-content">
<div class="left">
<div class="section-heading">Articles for .NET developers</div>
<h1 class="title">Visual Studio Toolbox Control Integration</h1>
<div class="content">
<script type="text/javascript">
/* <![CDATA[ */
(function() {
var s = document.createElement('script');
var t = document.getElementsByTagName('script')[0];
s.type = 'text/javascript';
s.async = true;
s.src = '//api.flattr.com/js/0.6/load.js?mode=auto&uid=libca&language=en_US';
t.parentNode.insertBefore(s, t);
})();
/* ]]> */
</script>
<div style="text-align: center; padding-top: 1em">
<a class="FlattrButton" href="visual-studio-toolbox-control-integration.html" title="article-toolbox-integration" lang="en-us" rel="flattr;uid:libca;title:article-toolbox-integration;language:en_US">
Visual Studio Toolbox Control Integration
</a>
</div>
<h1>The Most Complete Guide to Visual Studio Toolbox Control Integration</h1>
<p><a href="mailto:libca@libca.cz?subject=article-vs-toolbox-integration">Libor
Tinka</a>, Lead Developer, ComponentOwl.com</p>
<p><a href="../comics/10.html">
<img height="212" src="../images/articles/vs-toolbox-integration/comics.png" width="627" /></a></p>
<h2>Contents</h2>
<p><a href="visual-studio-toolbox-control-integration.html#introduction">1. Introduction</a><br />
<a href="visual-studio-toolbox-control-integration.html#prerequisites">2. Prerequisites</a><br />
<a href="visual-studio-toolbox-control-integration.html#sample-control">3. Creating a Sample Control</a><br />
<a href="visual-studio-toolbox-control-integration.html#integration-manual">4. Manual Toolbox Integration</a><br />
<a href="visual-studio-toolbox-control-integration.html#integration-tci">5. Toolbox Integration using TCI</a><br />
<a href="visual-studio-toolbox-control-integration.html#integration-dte">6. Toolbox Integration using DTE</a><br />
<a href="visual-studio-toolbox-control-integration.html#integration-vsi">7. Toolbox Integration using VSI Packages</a><br />
<a href="visual-studio-toolbox-control-integration.html#integration-vspackage">8. Toolbox Integration using VSPackages</a><br />
<a href="visual-studio-toolbox-control-integration.html#integration-vsix">9. Toolbox Integration using VSIX Packages</a><br />
<a href="visual-studio-toolbox-control-integration.html#net-versions">10. Supporting Multiple Version of .NET Framework</a><br />
<a href="visual-studio-toolbox-control-integration.html#source">11. Sample Source Code</a></p>
<h2><a name="introduction">1. Introduction</a></h2>
<p>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.</p>
<p>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.</p>
<p>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:</p>
<ul class="common">
<li>How to install control in Visual Studio Toolbox?</li>
<li>How to update the control?</li>
<li>How to uninstall/remove the control?</li>
<li>How to support multiple Visual Studio versions?</li>
</ul><br />
<p>There are several options on how to integrate your controls with Visual
Studio Toolbox:</p>
<ul class="common">
<li>Manual installation</li>
<li>Toolbox Control Installer (TCI)</li>
<li>Visual Studio Automation Object Model (DTE)</li>
<li>VSPackage</li>
<li>VSI package</li>
<li>VSIX package</li>
</ul>
<h3>Manual installation</h3>
<p>The simplest way of adding control into Visual Studio Toolbox is from within
the IDE.</p>
<p>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.</p>
<p><strong>Advantages:</strong> zero effort<br />
<strong>Disadvantages:</strong> require experienced users, slows user
producitivity, updating controls is not intuitive</p>
<h3>Toolbox Control Installer (TCI)</h3>
<p>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.</p>
<p><strong>Advantages:</strong> simple and fast component installation, updating
and removing<br />
<strong>Disadvantages:</strong> requires installation in GAC (not always
wanted), VS 2005 supported with SDK only</p>
<h3>Visual Studio Automation Object Model (DTE)</h3>
<p>If you are not afraid of COM, you can try DTE (Development Tools Environment) approach.
There is already a project on CodePlex called
<a href="http://vstudiotoolbox.codeplex.com/">Visual Studio Toolbox Manager</a>,
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 <strong>DteToolboxInstaller</strong>, which
is also a command-line application and <em>does</em> support Visual Studio 2013, 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.</p>
<p><strong>Advantages:</strong> does not require updating registry or GAC, full
control over Toolbox<br />
<strong>Disadvantages:</strong> very slow, separate installation required for
every version of Visual Studio</p>
<h3>VSPackage</h3>
<p>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.</p>
<p><strong>Advantages:</strong> quick installation, appearance in About box and
other extensibility features<br />
<strong>Disadvantages:</strong> cmplicated setup, each component requires its
own package if shipped separately</p>
<h3>VSI Package</h3>
<p>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.</p>
<p><strong>Advantages:</strong> simple creation, installer provided by Visual
Studio, automated creation and signing requires several specific steps<br />
<strong>Disadvantages:</strong> no quiet mode (extra steps when custom installer
is used), manual uninstallation</p>
<h3>VSIX Package</h3>
<p>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: <em>"This extension does not contain a digital signature."</em>
Even better news: The VsixInstaller supports quiet mode!</p>
<p>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.</p>
<p><strong>Advantages:</strong> installer provided by Visual Studio, quiet mode,
fast installation<br />
<strong>Disadvantages:</strong> package project required, automated creation is
complicated, no support for VS 2005 and 2008</p>
<h3>Comparison of Approaches</h3>
<p>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.</p>
<table style="width: 100%">
<tr>
<td>&nbsp;</td>
<td><b>2005</b></td>
<td><b>2008</b></td>
<td><b>2010</b></td>
<td><b>2012</b></td>
<td><b>2013</b></td>
<td><b>Speed</b></td>
<td><b>Install Automation</b></td>
<td><b>Uninstall automation</b>&nbsp;</td>
</tr>
<tr>
<td><strong>Manual installation</strong></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td>depends on user</td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" /></td>
</tr>
<tr>
<td><b>TCI</b></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-warning.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td>fast</td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-na.png" width="16" />&nbsp;</td>
</tr>
<tr>
<td><b>EnvDTE</b></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td>slow</td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" />&nbsp;</td>
</tr>
<tr>
<td><b>VSI</b></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td>moderate</td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" />&nbsp;</td>
</tr>
<tr>
<td><b>VSIX</b></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td>moderate (faster than VSI)</td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-no.png" width="16" />&nbsp;</td>
</tr>
<tr>
<td><b>VSPackage</b></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td>fast</td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" /></td>
<td><img height="16" src="../images/articles/vs-toolbox-integration/comparison-yes.png" width="16" />&nbsp;</td>
</tr>
</table>
<h2><a name="prerequisites">2. Prerequisites</a></h2>
<p>We will focus on integration with Visual Studio 2010, 2012 and 2013. Hence you will need:</p>
<ul class="common">
<li><strong>Visual Studio 2010 (or 2012, 2013)</strong></li>
<li><strong>Visual Studio 2010 SDK (or 2012 SDK, 2013 SDK)</strong></li>
<li><strong>Microsoft Windows SDK</strong></li>
</ul>
<p>The VS SDK contains regpkg.exe tool and project templates discussed in VSIX
and VSPackage approaches.</p>
<p>The Windows SDK contains gacutil.exe, guidgen.exe, signtool.exe and other
useful tools.</p>
<p>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:</p>
<table style="width: 100%">
<tr>
<td><strong>Release name</strong></td>
<td><strong>Version number</strong></td>
</tr>
<tr>
<td>Visual Studio 2005</td>
<td>8.0</td>
</tr>
<tr>
<td>Visual Studio 2008</td>
<td>9.0</td>
</tr>
<tr>
<td>Visual Studio 2010</td>
<td>10.0</td>
</tr>
<tr>
<td>Visual Studio 2012</td>
<td>11.0</td>
</tr>
<tr>
<td>Visual Studio 2013</td>
<td>12.0</td>
</tr>
</table>
<h2><a name="sample-control">3. Creating a Sample Control</a></h2>
<p>We will start by creating simple WinForms control for integration in VS
Toolbox.</p>
<p>You can start with File - New - Project... (Control+Shift+N) and select
<strong>Windows Forms Controls Library</strong> template.</p>
<p>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 <strong>Control</strong> will
suffice.</p>
<p>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
<em>unless</em> you use some feature specific to 4.5.</p>
<p>If you have multiple controls in your assembly and don't want to use some of
them in Toolbox, decorate them with <strong>ToolboxItem</strong> attribute with
<strong>defaultType</strong> parameter set to <strong>false</strong>:</p>
<pre>
<strong>[ToolboxItem(false)]</strong>
public class InvisibleControl : UserControl
{
...
}
</pre>
<p>I have created a very simple control called <strong>SampleControl</strong>:</p>
<p><img height="173" src="../images/articles/vs-toolbox-integration/sample-control.png" width="173" /></p>
<p>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).</p>
<h3>Custom Transparent Icon for the Toolbox</h3>
<p>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):</p>
<p><img height="128" src="../images/articles/vs-toolbox-integration/toolbox-icon.png" width="128" /></p>
<p>The icon file should have same name as the control class (i.e. <strong>
SampleControl.BMP</strong>).</p>
<p>Finally, use <strong>ToolboxBitmapAttribute</strong> to link icon with the control class:</p>
<pre>
<strong>[ToolboxBitmap(typeof(SampleControl), "Resources.SampleControl.bmp")]</strong>
public partial class SampleControl : UserControl
{
...
}
</pre>
<p>Note that icon location matters, at least in C#. Since I have added
the icon under custom folder named <strong>Resources</strong>, I need to reference
<strong>Resources.SampleControl.bmp</strong> instead of just <strong>SampleControl.bmp</strong>.</p>
<p>Here is the resulting transparent icon in Toolbox:</p>
<p><img height="337" src="../images/articles/vs-toolbox-integration/toolbox-2.png" width="254" /></p>
<h3>Marking the Control as Toolbox Item</h3>
<p>We can mark control as toolbox item by adding a <strong>ToolboxItemAttribute </strong>
with<strong> defaultType </strong>parameter set to <strong>true</strong>:</p>
<pre>
<strong>[ToolboxItem(true)]</strong>
public partial class SampleControl : UserControl
...
</pre>
<p>This decoration is optional since the controls within assembly are
considered toolbox items by default. However, we can mark certain control
classes with <strong>ToolboxItem(false)</strong> 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.</p>
<h3>Signing the Assembly</h3>
<p>The assembly containing controls (<strong>SampleControl.dll</strong> 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.</p>
<p>To give an assembly a strong name, open project properties and find <strong>
Signing</strong> tab:</p>
<p><img height="236" src="../images/articles/vs-toolbox-integration/sample-control-sign.png" width="774" /></p>
<p>Check the "<strong>Sign the assembly</strong>" option and select "<strong>&lt;New...&gt;</strong>"
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).</p>
<p>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.</p>
<h2><a name="integration-manual">4. Manual Toolbox Integration</a></h2>
<h3>Installing</h3>
<p>To install component into Visual Studio Toolbox manually, open some form or
control in designer, open the <strong>Toolbox</strong> window (Control+Alt+X), right-click on the
Toolbox window and select "<strong>Choose Items...</strong>":</p>
<p><img height="225" src="../images/articles/vs-toolbox-integration/toolbox-choose-items.png" width="328" /></p>
<p>The "<strong>Choose Toolbox Items</strong>" dialog will show up:</p>
<p><img height="483" src="../images/articles/vs-toolbox-integration/choose-toolbox-items.png" width="673" /></p>
<p>You can browse for DLL file with your component by clicking the "<strong>Browse...</strong>"
button.</p>
<p>This is the simplest way of putting component in the Toolbox without extra
actions required.</p>
<p>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.</p>
<h3>Making the Control Visible in "Choose Toolbox Items" Dialog Box</h3>
<p>As you can see on the above picture, the SampleControl component is already
displayed in the dialog box under ".NET Framework Components" tab.</p>
<p>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.</p>
<p>You can register your own assembly this way by creating a key in registry:</p>
<pre>32-bit OS: HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\<strong>&lt;your control name&gt;</strong>
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AssemblyFolders\<strong>&lt;your control name&gt;</strong></pre>
<p>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):</p>
<pre>32-bit OS: HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\<strong>&lt;your control name&gt;</strong>
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\<strong>&lt;your control name&gt;</strong></pre>
<p>In both cases, the default value for the key is a string with full path to
the folder with your assembly.</p>
<p>You can specify Toolbox tab in which the component should show up by adding
subkey named "<strong>Toolbox</strong>" with single string value "<strong>TabName</strong>" 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:</p>
<p>
<img height="265" src="../images/articles/vs-toolbox-integration/registry-assemblyfolders-toolbox.png" width="511" /></p>
<p>The control should also appear in "Choose Toolbox Items" dialog box if it
is installed in Global Assembly Cache.</p>
<p>The control pops up in the Toolbox automatically in
its own tab in Visual Studio 2012/2013.</p>
<h3>Installing the Control in GAC</h3>
<p>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).</p>
<p>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 <strong>true</strong> 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.</p>
<p>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.</p>
<p>You can work with GAC by using tool called <strong>gacutil.exe</strong> or
from code. We will discuss both approaches.</p>
<p>The gacutil.exe is located in Microsoft Windows SDK directory. There are two
such extecutables:</p>
<pre>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</pre>
<p>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.</p>
<p>You can install an assembly to GAC by calling:</p>
<pre>gacutil.exe /i SampleControl.dll</pre>
<p>To uninstall it, we refer to our assembly by its assembly name, not file
name:</p>
<pre>gacutil.exe /u SampleControl</pre>
<p>Finally, you can check if the assembly is installed in GAC by listing any
instances of the provided name:</p>
<pre>gacutil.exe /l SampleControl</pre>
<p>It is not wise, however, to use <strong>gacutil.exe</strong> from a custom
installer as it is located in SDK that user might not have installed.
Furthermore, the SDK license does not allow bundling <strong>gacutil.exe</strong>
with your installer.</p>
<p>Some installers like Inno Setup or MSI allow installing in GAC anyway.</p>
<p>You can also work with GAC using <strong>
System.EnterpriseServices.Internal.Publish</strong> class. The class have two
methods: <strong>GacInstall</strong> and <strong>GacRemove</strong>. Both
methods take just path to assembly file as a parameter, so for example:</p>
<pre>(new Publish()).GacInstall(assemblyPath);</pre>
<p>will install the specified assembly in GAC.</p>
<h3>Updating</h3>
<p>Updating the control depends on how it is installed and referenced.</p>
<p>If you have added component in the Toolbox manually via "Choose Toolbox Items"
dialog box and "Browse..." button, i.e. as a <em>file reference</em>, 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
<strong>true</strong> in reference properties window; the default is <strong>
false</strong> in this case). Simply replacing the DLL with the control by a newer
file will suffice. If the user have specified "Specific Version" to <strong>true</strong>,
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.</p>
<p>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 <strong>true</strong> by default:</p>
<p><img height="391" src="../images/articles/vs-toolbox-integration/reference-properites.png" width="372" /></p>
<p>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.</p>
<p>If you remove all versions of the component from GAC (e.g. using <strong>
gacutil.exe</strong>) and then install just the newest one, the build will break
unless the user changed "Specific Version" property to <strong>false</strong>.</p>
<h3>Removing</h3>
<p>Removing the manually installed control consists of just reverting all the
steps done during the installation.</p>
<p>In case of file references, deleting the file is sufficient.</p>
<p>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 <strong>gacutil.exe</strong>).</p>
<h3>Resetting Toolbox and Clearing the Toolbox Cache</h3>
<p>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.</p>
<p>To do that, right-click on the Toolbox window and select "<strong>Reset
Toolbox</strong>". Visual Studio will go through all the installed packages and reloads
components into the Toolbox.</p>
<p>If this won't help, you can perform <em>hard reset</em> of the Toolbox. Exit
Visual Studio and delete all .TBD files in the following folder:</p>
<pre>\Users\&lt;user&gt;\AppData\Local\Microsoft\VisualStudio\10.0\</pre>
<p>It should be up to four files:</p>
<p><img height="242" src="../images/articles/vs-toolbox-integration/files-toolbox-cache.png" width="499" /></p>
<p>Once removed, start Visual Studio again. After showing the Toolbox, all items
should load instead of loading only the cached versions.</p>
<h2><a name="integration-tci">5. Toolbox Integration using TCI</a></h2>
<h3>Installing</h3>
<p>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.</p>
<p>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:</p>
<pre>32-bit OS: HKLM\SOFTWARE\Microsoft\VisualStudio\10.0\Packages\<strong>{2c298b35-07da-45f1-96a3-be55d91c8d7a}</strong>
64-bit OS: HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\Packages\<strong>{2c298b35-07da-45f1-96a3-be55d91c8d7a}</strong></pre>
<p>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).</p>
<p>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.</p>
<p>The installation consists of putting the control in GAC (see previous
section for more information) and creating registry keys.</p>
<p>Suppose we have the <strong>SampleControl</strong> installed in GAC:</p>
<p><img height="186" src="../images/articles/vs-toolbox-integration/tci-gac.png" width="677" /></p>
<p>We will make reference to this assembly from registry by creating the
following key:</p>
<pre>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</pre>
<p>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".</p>
<h3>Installing in Visual Studio 2012 and 2013</h3>
<p>One extra step is required to make this work in Visual Studio 2012/2013, which is
adding the registry key also in its user config hive, i.e.:</p>
<pre>HKCU\Software\Microsoft\VisualStudio\11.0_Config\ToolboxControlsInstaller\SampleControl, Version=3.7.0.0, Culture=neutral, PublicKeyToken=3cc4c7b61201d46c</pre>
<p>for VS 2012. Use <strong>12.0_Config</strong> for VS 2013.</p>
<h3>Updating</h3>
<p>Updating the component is very simple. Just modify the above registry keys by
changing the version number.</p>
<h3>Removing</h3>
<p>To remove the component, delete the above registry keys. You should also
remove the corresponding assembly from GAC.</p>
<h3>Automating Integration with TCI using TciToolboxInstaller</h3>
<p>I made a simple command-line application called <strong>TciToolboxInstaller</strong>
which does all the described steps. The usage is simple:</p>
<pre>TciToolboxInstaller.exe [install|uninstall] [vs2005|vs2008|vs2010|vs2012|vs2013] [tab name] [assembly path]</pre>
<p>For example, if you like to install SampleControl.dll in Visual Studio 2012
Toolbox, just call:</p>
<pre>TciToolboxInstaller.exe install vs2012 "Component Owl" SampleControl.dll</pre>
<p>You can use quotes for the last two parameters if they contain spaces.</p>
<p>The <strong>TciToolboxInstaller</strong> project is contained in sample
source code.</p>
<h2><a name="integration-dte">6. Toolbox Integration using DTE</a></h2>
<h3>Installing</h3>
<p>The
<a href="http://www.codeproject.com/Articles/477550/Developing-extension-packages-for-Visual-Studio-20">DTE</a> (Development Tools Environment) approach does not require working
with GAC or registry. It remotely manipulates Visual Studio Toolbox and
adds/removes items as needed.</p>
<p>The whole installation is done from (managed) code using COM wrappers. It
works in the following steps:</p>
<ul class="common">
<li>Check if an instance of Visual Studio is not running. If not, continue.</li>
<li>Retrieve an <strong>EnvDTE.DTE</strong> object corresponding to the
version of Visual Studio we want to integrate with.</li>
<li>Create a "dummy" project using the <strong>DTE</strong> object</li>
<li>Obtain Toolbox window and <strong>ToolBox</strong> object from it.</li>
<li>Find or create <strong>ToolBoxTab</strong> object.</li>
<li>Add item in the Toolbox tab (<strong>ToolBoxTab.ToolBoxItems.Add</strong>).</li>
<li>Wait until current instance of Visual Studio stops running.</li>
</ul>
<p>Here are some of the step/strongs in C# code - it is an excerpt from <strong>
DteToolboxInstaller</strong> project provided in sample source code:</p>
<pre>
// obtain a DTE object
Type typeDTE = Type.GetTypeFromProgID(&quot;VisualStudio.DTE.11.0&quot;);
DTE dte = (DTE)Activator.CreateInstance(typeDTE, true);
// create a temporary file
string tempFile = Path.GetFileNameWithoutExtension(Path.GetTempFileName());
string tempDirectory = string.Format(&quot;{0}{1}&quot;, Path.GetTempPath(), tempFile);
// create Visual Studio Solution
Solution4 solution = (dte.Solution as Solution4);
string templatePath = solution.GetProjectTemplate(TemplateName, &quot;CSharp&quot;);
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);
}
</pre>
<p>There are several obstacles on implementing the DTE approach.</p>
<p>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.</p>
<p>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.</p>
<p>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 <strong>
DteToolboxInstaller</strong> (see below) source code, where is a working installer
implemented that uses this approach.</p>
<h3>Updating and Removing</h3>
<p>Since we have full control over the Toolbox with this approach, updating or
removing items/tabs is done with the corresponding DTE objects.</p>
<h3>Automatic Integration with DTE using DteToolboxInstaller</h3>
<p>I made a simple command-line application called <strong>DteToolboxInstaller</strong>
which does all the necessary steps and solves the deals with the discussed
obstacles. The usage is simple:</p>
<pre>DteToolboxInstaller.exe [install|uninstall] [vs2005|vs2008|vs2010|vs2012|vs2013] [tab name] [assembly path]</pre>
<p>For example, if you like to install SampleControl.dll in Visual Studio 2012
Toolbox, just call:</p>
<pre>DteToolboxInstaller.exe install vs2012 "Component Owl" SampleControl.dll</pre>
<p>You can use quotes for the last two parameters if they contain spaces.</p>
<p>The <strong>DteToolboxInstaller</strong> project is contained in sample
source code.</p>
<h2><a name="integration-vsi">7. Toolbox Integration using VSI Packages</a></h2>
<p>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.</p>
<p>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.</p>
<h3>Creating the VSI Package</h3>
<p>I have created an empty folder and copied <strong>SampleControl.dll</strong> in
it. All that is needed to make a VSI package is to create a .VSCONTENT file,
which is simply a XML file satisfying
<a href="http://msdn.microsoft.com/en-us/library/aa992029.aspx">Visual Studio Content Installer schema</a>:</p>
<pre>
&lt;VSContent xmlns=&quot;http://schemas.microsoft.com/developer/vscontent/2005&quot;&gt;
&lt;Content&gt;
&lt;FileName&gt;SampleControl.dll&lt;/FileName&gt;
&lt;DisplayName&gt;SampleControl&lt;/DisplayName&gt;
&lt;Description&gt;ComponentOwl.com SampleControl&lt;/Description&gt;
&lt;FileContentType&gt;Toolbox Control&lt;/FileContentType&gt;
&lt;ContentVersion&gt;2.0&lt;/ContentVersion&gt;
&lt;/Content&gt;
&lt;/VSContent&gt;
</pre>
<p>The content is readable and pretty straightforward. The <strong>
ContentVersion</strong> element can contain either "1.0" (support for Visual
Studio 2005, 2008 and 2010) or "2.0" (support for Visual Studio 2008, 2010,
2012, 2013).</p>
<p>Now we zip the two files and rename extension of the archive to .VSI. We
should end up with the following three files:</p>
<p><img height="74" src="../images/articles/vs-toolbox-integration/files-vsi.png" width="631" /></p>
<p>If you double-click the SampleControl.vsi, the Visual Studio
Content Installer opens up. You can start the installer from command line as
well:</p>
<pre>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</pre>
<p>The installer have a form o wizard:</p>
<p><img height="282" src="../images/articles/vs-toolbox-integration/vsi-installer-default.png" width="317" /></p>
<h3>Signing the VSI Package</h3>
<p>By default, the VSI package is not signed. This causes showing: "<strong>Publisher:
Unknown</strong>" label on the first page of the installation wizard and an unpleasant
dialog box later on:</p>
<p><img height="282" src="../images/articles/vs-toolbox-integration/vsi-installer-unsigned.png" width="317" /></p>
<p>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).</p>
<p>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 <strong>MakeZipExe</strong>
to do this task:</p>
<pre>MakeZipExe.exe -zipfile:SampleControl.vsi -output:SampleControl-unsigned.vsi -overwrite</pre>
<p>The <strong>MakeZipExe</strong> tool is located at Visual Studio's binary
folder:</p>
<pre>c:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\MakeZipExe.exe</pre>
<p>The second step is signing the .EXE file using <strong>signtool.exe</strong>.
You can find <strong>signtool.exe</strong> in Microsoft Windows SDK, i.e.:</p>
<pre>c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe</pre>
<p>Here is a sample usage of <strong>signtool.exe</strong>:</p>
<pre>signtool.exe sign /du "http://www.componentowl.com/sample-control" /f certificate.pfx /p abc123 /t "http://timestamp.comodoca.com/authenticode" "SampleControl-signed.vsi"</pre>
<p>When signed, the "<strong>Publisher</strong>" and "<strong>Information URL</strong>" 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).</p>
<p>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.
<a href="http://www.winzip.com">WinZip</a>).</p>
<h3>Uninstalling the Control from Toolbox</h3>
<p>As far as I know, the control cannot be removed from the Toolbox
programmatically. You need to delete the control's DLL located at</p>
<pre>c:\Users\&lt;user&gt;\Documents\Visual Studio 2010\Controls\</pre>
<p>the same should be done for every version of Visual Studio installed.</p>
<p>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:</p>
<p><img height="274" src="../images/articles/vs-toolbox-integration/vsi-choose-items.png" width="673" />&nbsp;</p>
<h3>Drawbacks of Using VSI Package</h3>
<p>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.</p>
<p>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.</p>
<p>You also cannot specify custom Toolbox tab. All controls are installed under
"<strong>My Controls</strong>" tab:</p>
<p><img height="318" src="../images/articles/vs-toolbox-integration/toolbox-vsi.png" width="165" /></p>
<h2><a name="integration-vspackage">8. Toolbox Integration using VSPackages</a></h2>
<p>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.</p>
<p>Although VSPackage and our sample control can be packed within the same
assembly, we will create a separate Visual Studio Package project.</p>
<p>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).</p>
<h3>Creating VSPackage Project</h3>
<p>We would like to have our VSPackage compatible with VS 2010, 2012
and 2013, so we will work in Visual Studio 2010.</p>
<p>Select "File - New - Project.." (Control+Shift+N) and
select the "<strong>Visual Studio Package</strong>" template:</p>
<p><img height="450" src="../images/articles/vs-toolbox-integration/template-vspackage.png" width="825" /> </p>
<p>This will start a "Visual Studio Package Wizard":</p>
<p><img height="197" src="../images/articles/vs-toolbox-integration/vspackage-wizard-1.png" width="258" /></p>
<p>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":</p>
<p><img height="197" src="../images/articles/vs-toolbox-integration/vspackage-wizard-2.png" width="258" /></p>
<p><img height="197" src="../images/articles/vs-toolbox-integration/vspackage-wizard-3.png" width="258" /></p>
<p>Now we will take a look on the generated files. Open the <strong>Guids.cs</strong>
file:</p>
<pre>
// Guids.cs
// MUST match guids.h
using System;
namespace ComponentOwl.ToolboxIntegration
{
static class GuidList
{
<strong>public const string GuidSampleVSPackagePkgString = "00000000-8fdf-48b6-98f8-4ff21a3a4def";</strong>
public const string GuidSampleVSPackageCmdSetString = "def6519d-5ace-4062-95d6-4ee43f4a5de9";
public static readonly Guid GuidSampleVSPackageCmdSet = new Guid(GuidSampleVSPackageCmdSetString);
};
}
</pre>
<p>Here are the <a href="http://en.wikipedia.org/wiki/GUID">GUIDs</a> 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.</p>
<p>You can also generate new GUIDs any time, for example using
<a href="http://www.guidgenerator.com/">online GUID
generator</a> or <strong>guidgen.exe</strong> utility from Windows SDK. When these numbers are changed, your package will be different from
Visual Studio's point of view.</p>
<p>Another important file here is <strong>source.extension.vsixmanifest</strong>.
If you double-click on the file in Solution Explorer, the VSIX Manifest Designer
will show up:</p>
<p><img height="347" src="../images/articles/vs-toolbox-integration/vsix-manifest-designer.png" width="454" /></p>
<p>Not all the fields are mandatory, but I will fill all of them nevertheless:</p>
<ul class="common">
<li><strong>ID</strong> - 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.</li>
<li><strong>Product Name</strong> - This field is used for Toolbox Tab name,
so I will put "Component Owl Controls" here.</li>
<li><strong>Author</strong> - Your name or company name -
"ComponentOwl.com", for example.</li>
<li><strong>Version</strong> - 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.</li>
<li><strong>Description</strong> - Speaks for itself.</li>
<li><strong>Locale</strong> - Language for the package.</li>
<li><strong>Supported VS Editions</strong> - Here you can specify which
editions of Visual Studio 2010 you would like to support. Of course, it can
support VS 2012/2013 as well, but for now I will just check Ultimate, Premium and
Professional editions.</li>
<li><strong>Supported Framework Runtime</strong> - 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.</li>
</ul>
<h3>Adding Support for Visual Studio 2012/2013</h3>
<p>We have specified supported Visual Studio Editions in VSIX Manifest Designer,
through the "Visual Studio Version and Edition" dialog box:</p>
<p><img height="220" src="../images/articles/vs-toolbox-integration/vs-editions-dialog.png" width="238" /></p>
<p>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/2013. We have to <strong>
source.extensions.vsixmanifest file</strong> for manual editing. Select the file
in Solution Explorer and press F7 (View Code):</p>
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;Vsix xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; Version=&quot;1.0.0&quot; xmlns=&quot;http://schemas.microsoft.com/developer/vsx-schema/2010&quot;&gt;
&lt;Identifier Id=&quot;aaaaaaaa-8fdf-48b6-98f8-4ff21a3a4def&quot;&gt;
&lt;Name&gt;SampleVsPackage&lt;/Name&gt;
&lt;Author&gt;ComponentOwl.com&lt;/Author&gt;
&lt;Version&gt;1.0&lt;/Version&gt;
&lt;Description xml:space=&quot;preserve&quot;&gt;Information about my package&lt;/Description&gt;
&lt;Locale&gt;1033&lt;/Locale&gt;
&lt;InstalledByMsi&gt;false&lt;/InstalledByMsi&gt;
&lt;SupportedProducts&gt;
<strong> &lt;VisualStudio Version=&quot;10.0&quot;&gt;
&lt;Edition&gt;Ultimate&lt;/Edition&gt;
&lt;Edition&gt;Premium&lt;/Edition&gt;
&lt;Edition&gt;Pro&lt;/Edition&gt;
&lt;/VisualStudio&gt;</strong>
&lt;/SupportedProducts&gt;
&lt;SupportedFrameworkRuntimeEdition MinVersion=&quot;4.0&quot; MaxVersion=&quot;4.5&quot; /&gt;
&lt;/Identifier&gt;
&lt;References&gt;
&lt;Reference Id=&quot;Microsoft.VisualStudio.MPF&quot; MinVersion=&quot;10.0&quot;&gt;
&lt;Name&gt;Visual Studio MPF&lt;/Name&gt;
&lt;/Reference&gt;
&lt;/References&gt;
&lt;Content&gt;
&lt;VsPackage&gt;|%CurrentProject%;PkgdefProjectOutputGroup|&lt;/VsPackage&gt;
&lt;/Content&gt;
&lt;/Vsix&gt;
</pre>
<p>Take a look on the <strong>Vsix/Identifier/SupportedProducts/VisualStudio</strong>
element (highlighted in bold). Copy and paste this element and modify <strong>
Version</strong> attribute on the second one to "11.0":</p>
<pre>
&lt;VisualStudio Version=&quot;11.0&quot;&gt;
&lt;Edition&gt;Ultimate&lt;/Edition&gt;
&lt;Edition&gt;Premium&lt;/Edition&gt;
&lt;Edition&gt;Pro&lt;/Edition&gt;
&lt;/VisualStudio&gt;
</pre>
<p>The edition tags are valid for version 11.0 because Visual Studio 2012
template generates the same edition names.</p>
<h3>Writing Package Code</h3>
<p>Now we will take a look on the VSPackage code itself. Open the <strong>
SampleVSPackage.cs</strong>
file. I kept only the necessary code and added the <strong>ProvideToolboxItems</strong> attribute:</p>
<pre>
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[Guid(GuidList.guidSampleVsPackagePkgString)]
<strong>[ProvideToolboxItems(1)]</strong>
public sealed class SampleVsPackage : Package
{
}
</pre>
<p>Our VSPackage implementation inherits from <strong>
Microsoft.VisualStudio.Shell.Package</strong> class and is decorated by three
attributes:</p>
<ul class="common">
<li><strong>PackageRegistrationAttribute</strong> - Specifies that package
registration tool should look for additional attributes (will be discussed
later).</li>
<li><strong>InstalledProductRegistrationAttribute</strong> - Provides
information for the Visual Studio splash screen and About box.</li>
<li><strong>GuidAttribute</strong> - 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).</li>
<li><strong>ProvideToolboxItemsAttribute</strong> - 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.</li>
</ul>
<p>The strings "#110" and "#112" in <strong>
InstalledProductRegistrationAttribute</strong> refer to keys in <strong>
VSPackage.resx</strong>. You can open this file and edit package name and
description there:</p>
<p><img height="92" src="../images/articles/vs-toolbox-integration/vspackage-resx-designer.png" width="422" /></p>
<p>Now we write methods within <strong>SampleVsPackage</strong> class that work with the Toolbox:</p>
<pre>
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);
}
</pre>
<p>The method names <strong>InstallToolboxItems</strong> and <strong>
RemoveToolboxItems</strong>
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.</p>
<p>The <strong>ToolboxService</strong> class comes from <strong>
System.Drawing.Design</strong> and we need to add reference to this asssembly in
order to use <strong>ToolboxService</strong>.</p>
<h3>Building the Package</h3>
<p>Before building the <strong>SampleVsPackage</strong> project, open project
properties, find the VSIX tab and uncheck all the options:</p>
<p><img height="225" src="../images/articles/vs-toolbox-integration/vsix-options.png" width="664" /></p>
<p>Finally, build the project. Just two files, <strong>SampleVsPackage.dll</strong>
and <strong>SampleVsPackage.pdb</strong>, should be generated.</p>
<h3>Registering the Package</h3>
<p>Until the package can be loaded by Visual Studio, it needs to be
<a href="http://msdn.microsoft.com/en-us/library/bb187332(v=vs.80).aspx">registered</a>.
The registration is simply writing specific keys into Windows Registry.</p>
<p>To do that, find the <strong>Package Registration Utility</strong> (RegPkg.exe). It should be
located in Visual Studio SDK directory, e.g.:</p>
<pre>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</pre>
<p>You can copy the tool where it suits you.</p>
<p>Here is a sample usage of RegPkg:</p>
<pre>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</pre>
<p>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.</p>
<p>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 <strong>/regfile</strong> parameter:</p>
<pre>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</pre>
<p>This creates <strong>SampleVsPackge.reg</strong> file you can use any time
later instead of RegPkg.exe itself.</p>
<p>There are two other options for specifying how the package will be registered:
<strong>codebase</strong> and <strong>assembly</strong>. When <strong>/codebase</strong>
parameter is used (as in the sample above), the registry will point to the location on disk where your
package is located (see
<a href="http://msdn.microsoft.com/en-us/library/system.reflection.assembly.codebase.aspx">
Assembly.CodeBase</a> property for more information).</p>
<p>Another option is the <strong>/assembly</strong> parameter - this assumbes
that your VSPackage assembly is located in GAC (Global Assembly Cache). See
section <em>Installing the Control in GAC</em> for more information.</p>
<p>You can check out the registry after the package registration:</p>
<p><img height="220" src="../images/articles/vs-toolbox-integration/registry-package.png" width="991" /></p>
<h3>Package Registration for Visual Studio 2012/2013</h3>
<p>Regrettably, simply registering package is not enough for Visual Studio 2012/2013 to load
it (see this
<a href="http://blogs.msdn.com/b/dsvst/archive/2012/10/08/your-vs-package-may-not-load-after-porting-it-to-vs-2012.aspx">
blog post</a>). Because of performance optimizations, VS developers removed
feature that looks for changes in VS registry root and thus we need to call</p>
<pre>devenv.exe /Setup</pre>
<p>In order to finish package registration.</p>
<p>This call can be very time consuming since Visual Studio 2012/2013 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/2013 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).</p>
<p>We can speed things up by a little hack. One of the things the <strong>/Setup</strong>
does is copying registry keys from HKLM to Visual Studio's 11.0_Config hive (or 12.0_Config, respectively). We can just write registry under this key
instead of calling devenv.exe and avoid possibly lengthy operation.</p>
<p>The hive is located in <strong>
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config</strong>.</p>
<p>So let's open and edit the <strong>SampleVsPackage.reg</strong> file we have
generated using <strong>RegPkg.exe</strong> earlier. Here is the modified version where only the
registry root has been changed:</p>
<pre>
REGEDIT4
[<strong>HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config</strong>\InstalledProducts\SampleVsPackage]
@="#110"
"Package"="{00000000-8fdf-48b6-98f8-4ff21a3a4def}"
"PID"="1.0"
"ProductDetails"="#112"
"LogoID"="#400"
[<strong>HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config</strong>\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"
[<strong>HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config</strong>\Packages\{00000000-8fdf-48b6-98f8-4ff21a3a4def}]
[<strong>HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config</strong>\Packages\{00000000-8fdf-48b6-98f8-4ff21a3a4def}\Toolbox]
"Default Items"=dword:00000001
</pre>
<p>The highlighted parts have been edited.</p>
<p>So in addition to standard package registration, we also write in the
registry where Visual Studio 2012/2013 user config hive resides. This is sufficient
for our VSPackage to load.</p>
<p>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 "<strong>devenv.exe /Setup</strong>".</p>
<h3>Loading VSPackage</h3>
<p>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:</p>
<p><img height="318" src="../images/articles/vs-toolbox-integration/toolbox-1.png" width="256" /></p>
<h3>Displaying Your Extension in VS About Box</h3>
<p>If you want information about your extension to be visible in Visual Studio
splash screen and About Box, implement <strong>IVsInstalledProduct</strong>
interface:</p>
<pre>
...
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;
}
...
}
</pre>
<p>This code causes the component to show up in the list of "Installed Products"
in Visual Studio about box:</p>
<p><img height="295" src="../images/articles/vs-toolbox-integration/about-box.png" width="395" /></p>
<p>As for the splash screen, Visual Studio 2008 used to display extensions in
its splash screen, but later version do not:</p>
<p><img height="160" src="../images/articles/vs-toolbox-integration/splash-2008.png" width="264" />&nbsp;&nbsp;&nbsp;
<img height="184" src="../images/articles/vs-toolbox-integration/splash-2010.png" width="264" />&nbsp;&nbsp;&nbsp;
<img height="264" src="../images/articles/vs-toolbox-integration/splash-2012.png" width="202" /></p>
<h3>Troubleshooting Package Load Failures</h3>
<p>You may encounter this dialog when playing with packages:</p>
<p><img height="319" src="../images/articles/vs-toolbox-integration/package-load-failure.png" width="494" /></p>
<p>When you click "No", the package will be skipped later when loading packages.
You can re-enable loading all packages by running</p>
<pre>devenv.exe /ResetSkipPkgs</pre>
<p>To debug package load problem, you can do just what the dialog says. Run</p>
<pre>devenv.exe /log</pre>
<p>and then take a look on the <strong>ActivityLog.xml</strong> (path is shown
in the dialog). There you can find cause of the problem in one of the "<strong>entry</strong>"
elements:</p>
<pre>
...
&lt;entry&gt;
&lt;record&gt;106&lt;/record&gt;
&lt;time&gt;2012/10/26 06:07:36.920&lt;/time&gt;
&lt;type&gt;Error&lt;/type&gt;
&lt;source&gt;VisualStudio&lt;/source&gt;
&lt;description&gt;CreateInstance failed for package [ComponentOwl.ToolboxIntegration.SampleVSPackage, SampleVSPackage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=87379c2b0cde9bc3]&lt;/description&gt;
&lt;guid&gt;{761F0CB7-64C1-4695-91D2-6E3C26C12314}&lt;/guid&gt;
&lt;hr&gt;80070002&lt;/hr&gt;
&lt;errorinfo&gt;<strong>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.</strong>&lt;/errorinfo&gt;
&lt;/entry&gt;
...
</pre>
<p>In this particular case, the problem was caused by changing output path from
"<strong>bin\SampleVSPackage.dll</strong>" to "<strong>bin\Debug\SampleVSPackage.dll</strong>"
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).</p>
<h3>Past Troubles with Package Load Keys</h3>
<p>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.</p>
<p>I believe PLK caused many troubles and headaches to developers, including
myself.</p>
<p>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</p>
<h3>Update Control Already Installed in Toolbox</h3>
<p>Suppose we have already integrated SampleControl version 3.3.0.0 in the
Visual Sudio Toolbox:</p>
<p><img height="337" src="../images/articles/vs-toolbox-integration/toolbox-update-1.png" width="290" /></p>
<p>We would like to update this control to version 3.4.0.0.</p>
<p>First of all, we update assembly information of the <strong>SampleControl</strong>
project:</p>
<p><img height="405" src="../images/articles/vs-toolbox-integration/assembly-info.png" width="407" /></p>
<p>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.</p>
<p>You don't need to increment assembly version of the SampleVsPackage project,
but at least you have to increment parameter of the <strong>ProvideToolboxItems</strong> attribute:</p>
<pre>
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[Guid(GuidList.guidSampleVsPackagePkgString)]
<strong>[ProvideToolboxItems(2)]</strong>
public sealed class SampleVsPackage : Package, IVsInstalledProduct
{
...
}
</pre>
<p>The package need to be re-registered (see section <em>Registering the Package</em>)
which will effectively update just the "Default Items" value in the Toolbox key:</p>
<p><img height="166" src="../images/articles/vs-toolbox-integration/registry-toolbox.png" width="720" /></p>
<p>This will cause Visual Studio to update your control in the Toolbox:</p>
<p><img height="337" src="../images/articles/vs-toolbox-integration/toolbox-update-2.png" width="287" /></p>
<h3>Remove Control from the Toolbox</h3>
<p>Now we would like to remove control from the Visual Studio Toolbox. This
step can be done by custom uninstaller.</p>
<p>One way to do that is to simply <em>unregister</em> the VSPackage using
RegPkg.exe:</p>
<pre>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</pre>
<p>You can also do this manually by simply removing the registry entry of the
corresponding package, e.g.:</p>
<pre>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}</pre>
<p>On Visual Studio 2012/2013, 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 <em>Package Registration for Visual Studio 2012/2013</em>.</p>
<h2><a name="integration-vsix">9. Toolbox Integration using VSIX Packages</a></h2>
<p>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.</p>
<p>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/2013. We want a VSIX
Package compatible with both, so we will work in Visual Studio 2010.</p>
<h3>Create a new Project from Template</h3>
<p>If you have Visual Studio 2010 SDK installed, you can create a new VSIX
package project with control from a template.</p>
<p>Select "File - New - Project.." (Control+Shift+N) and
then select the "<strong>Windows Forms Toolbox Control</strong>" or "<strong>WPF
Toolbox Control</strong>" template:</p>
<p><img height="450" src="../images/articles/vs-toolbox-integration/template-toolbox-control.png" width="823" /></p>
<p>The projects is basically a VSPackage wrapped in VSIX container after
build. The package assembly also contain the control class named <strong>
ToolboxControl</strong>.</p>
<p>There are three important files generated by the template:</p>
<ul class="common">
<li><strong>ProvideToolboxControlAttribute.cs</strong> - This is attribute
for ToolboxControl class. We will discuss it later.</li>
<li><strong>source.extension.vsixmanifest</strong> - This is manifest XML
file for our VSIX package. It contains all information about the package and
what it contains.</li>
<li><strong>ToolboxControl.cs</strong> - This is a sample control to be
installed in Visual Studio Toolbox.</li>
</ul>
<h3>Create a new Project from VSPackage</h3>
<p>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 <strong>SampleVsixPackage</strong> and configure it according
to "Windows Forms Toolbox Control" template to show you all the differences.</p>
<p>The basic configuration of <strong>source.extension.vsixmanifest</strong> is
the same as in previous chapter.</p>
<p>Project properties differs from VSPackage we have created earlier on the VSIX
tab, where we have the first two check boxes checked:</p>
<p><img height="225" src="../images/articles/vs-toolbox-integration/vsix-options.png" width="664" /></p>
<h3>Update the Manifest File</h3>
<p>Double-click on the <strong>source.extension.vsixmanifest</strong> file to
open up the <a href="http://msdn.microsoft.com/en-us/library/ee943167.aspx">VSIX Manifest Designer</a>:</p>
<p><img height="347" src="../images/articles/vs-toolbox-integration/vsix-manifest-designer.png" width="454" /></p>
<p>If you are not sure about some part of the form, please take a look on
section <em>Create VSPackage Project</em>, where the form is described in more
detail.</p>
<p>In addition to previous VSPackage project, I have also filled the following
optional boxes:</p>
<ul class="common">
<li><strong>License Terms</strong> - If you have EULA or other licence in
TXT or RTF format, you can browse for it.</li>
<li><strong>Icon</strong> - You can browse for an icon representing the
extension. It should be 32x32 pixels large, PNG, BMP, JPEG or ICO image
format.</li>
<li><strong>Preview Image</strong> - Thumbnail image representing the
extension. It should be 200x200 pixels large, PNG, BMP or JPEG image format.</li>
<li><strong>More Info URL</strong> - URL of a website containing more
information about the extension.</li>
<li><strong>Getting Started Guide</strong> - URL of a website with
documentation; you can also provide relative path to HTML file with the
local documentation.</li>
</ul>
<h3>Add Control</h3>
<p>Let's create a new WPF control within the <strong>SampleVsixPackage</strong>
project itself. I will name it <strong>SampleWpfControl</strong> to distinguish
it from <strong>SampleControl</strong> we have created earlier.</p>
<p>To ensure our control will show up in Toolbox of Visual Studio 2012/2013, we have
to decorate the <strong>SampleWpfControl</strong> class by <strong>
ProvideToolboxControlAttribute</strong>:</p>
<pre>
<strong>[ProvideToolboxControl(&quot;SampleWpfControl&quot;, true)]</strong>
public partial class SampleWpfControl : UserControl
{
...
}
</pre>
<p>You also have to provide implementation of the attribute class:</p>
<pre>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
[System.Runtime.InteropServices.ComVisibleAttribute(false)]
public sealed class ProvideToolboxControlAttribute : RegistrationAttribute
{
private const string ToolboxControlsInstallerPath = &quot;ToolboxControlsInstaller&quot;;
public ProvideToolboxControlAttribute(string name, bool isWpfControls)
{
if (name == null)
{
throw new ArgumentException(&quot;name&quot;);
}
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(&quot;context&quot;);
}
using (Key key = context.CreateKey(String.Format(CultureInfo.InvariantCulture, &quot;{0}\\{1}&quot;,
ToolboxControlsInstallerPath,
context.ComponentType.Assembly.FullName)))
{
key.SetValue(String.Empty, this.Name);
key.SetValue(&quot;Codebase&quot;, context.CodeBase);
if (this.IsWpfControls)
{
key.SetValue(&quot;WPFControls&quot;, &quot;1&quot;);
}
}
}
public override void Unregister(RegistrationAttribute.RegistrationContext context)
{
if (context != null)
{
context.RemoveKey(String.Format(CultureInfo.InvariantCulture, &quot;{0}\\{1}&quot;,
ToolboxControlsInstallerPath,
context.ComponentType.AssemblyQualifiedName));
}
}
}
</pre>
<p>This code is generated if you create project from template.</p>
<p>The project in Solution Explorer should look like this:</p>
<p>
<img height="348" src="../images/articles/vs-toolbox-integration/solution-explorer-samplevsixpackage.png" width="260" /></p>
<h3>Adding Controls from Other Projects</h3>
<p>What if we would like to use <strong>SampleControl.dll</strong> as in the VSI
package scenario?</p>
<p>Of course, we can click "Add Content" in the VSIX Manifest designer and
simply add "Toolbox Control" content from other project:</p>
<p><img height="338" src="../images/articles/vs-toolbox-integration/vsix-designer-add-content.png" width="469" /></p>
<p>However, this is possible only if the <strong>SampleControl</strong> project
itself is a package project!</p>
<p>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.</p>
<h3>Setting Up VSIX Installer</h3>
<p>The VSIX Installer tool (<strong>VsixInstaller.exe</strong>) is located in
Visual Studio's binary folder:</p>
<pre>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</pre>
<p>You can hit F6 and Visual Studio will build your project to create <strong>
SampleVsixPackage.vsix</strong> file. This is our VSIX package.</p>
<p>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.</p>
<p>The installation can fail in the very first step:</p>
<p><img height="293" src="../images/articles/vs-toolbox-integration/vsix-installer-1.png" width="450" /></p>
<p>This problem appears if you have invalid manifest file. If this happen, open
<strong>source.extension.vsixmanifest</strong> and fill in all missing data.
Furthermore, you can check if the XML is valid according to
<a href="http://msdn.microsoft.com/en-us/library/dd393700(v=vs.100).aspx">VSIX Extension
Schema</a>.</p>
<p>Now we are able to rebuild and run the VSIX installer again:</p>
<p><img height="338" src="../images/articles/vs-toolbox-integration/vsix-installer-2.png" width="450" /></p>
<h3>Make the VSIX Package Compatible with Visual Studio 2012/2013</h3>
<p>To make our VSIX package working with Visual Studio 2012 and newer, we need
to manually update the manifest file. Select the <strong>
source.extension.vsixmanifest</strong> file and press F7 (View Code):</p>
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;Vsix xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; Version=&quot;1.0.0&quot; xmlns=&quot;http://schemas.microsoft.com/developer/vsx-schema/2010&quot;&gt;
&lt;Identifier Id=&quot;ComponentOwl.ToolboxControl.Express&quot;&gt;
&lt;Name&gt;Component Owl&lt;/Name&gt;
&lt;Author&gt;ComponentOwl.com&lt;/Author&gt;
&lt;Version&gt;1.0&lt;/Version&gt;
&lt;Description xml:space=&quot;preserve&quot;&gt;Windows Forms Toolbox Control&lt;/Description&gt;
&lt;Locale&gt;1033&lt;/Locale&gt;
&lt;MoreInfoUrl&gt;http://www.componentowl.com/toolbox-control&lt;/MoreInfoUrl&gt;
&lt;License&gt;license.rtf&lt;/License&gt;
&lt;GettingStartedGuide&gt;http://www.componentowl.com/documentation/toolbox-control&lt;/GettingStartedGuide&gt;
&lt;Icon&gt;icon.png&lt;/Icon&gt;
&lt;PreviewImage&gt;overview.jpg&lt;/PreviewImage&gt;
&lt;SupportedProducts&gt;
&lt;VisualStudio Version=&quot;10.0&quot;&gt;
&lt;Edition&gt;Ultimate&lt;/Edition&gt;
&lt;Edition&gt;Premium&lt;/Edition&gt;
&lt;Edition&gt;Pro&lt;/Edition&gt;
&lt;/VisualStudio&gt;
<strong> &lt;VisualStudio Version=&quot;11.0&quot;&gt;
&lt;Edition&gt;Ultimate&lt;/Edition&gt;
&lt;Edition&gt;Premium&lt;/Edition&gt;
&lt;Edition&gt;Pro&lt;/Edition&gt;
&lt;/VisualStudio&gt;</strong>
<strong> &lt;VisualStudio Version=&quot;12.0&quot;&gt;
&lt;Edition&gt;Ultimate&lt;/Edition&gt;
&lt;Edition&gt;Premium&lt;/Edition&gt;
&lt;Edition&gt;Pro&lt;/Edition&gt;
&lt;/VisualStudio&gt;</strong>
&lt;/SupportedProducts&gt;
&lt;SupportedFrameworkRuntimeEdition MinVersion=&quot;4.0&quot; MaxVersion=&quot;5.0&quot; /&gt;
&lt;/Identifier&gt;
&lt;References /&gt;
&lt;Content&gt;
&lt;ToolboxControl&gt;|%CurrentProject%;PkgdefProjectOutputGroup|&lt;/ToolboxControl&gt;
&lt;/Content&gt;
&lt;/Vsix&gt;
</pre>
<p>The bolded text have been added. I have simply added a new <strong>
VisualStudio</strong> element with higher version and all the editions (they are
relevant for VS 2012 and 2013 as its own template also generates these).</p>
<p>The VSIX installer will show Visual Studio 2012 options as
well after this update (if installed, of course):</p>
<p><img height="354" src="../images/articles/vs-toolbox-integration/vsix-installer-3.png" width="450" /></p>
<p>Please note that Visual Studio 2012/2013 also works with 2.0 version of the
schema, so if you create VSIX package in Visual Studio 2012/2013, 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.</p>
<p>A great advantage over VSI package is that installation of Toolbox control is
really fast with VSIX.</p>
<p>Regrettably, the control won't show up in Visual Studio 2012/2013 Toolbox in its
default configuration. You need to enable loading-per user extensions (this
option is enabled by default in Visual Studio 2010):</p>
<p><img height="199" src="../images/articles/vs-toolbox-integration/vsix-per-user-extensions.png" width="757" /></p>
<h3>Signing the VSIX Package</h3>
<p>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.</p>
<p>To sign a VSIX package, we need <strong>PackageSignatureManager</strong> from
<strong>System.IO.Packaging</strong> (<strong>WindowsBase.dll</strong>). I made a simple command-line application called <strong>SignVsix</strong> (you can
find it in sample source code) that takes three arguments (VSIX file path, PFX
certificate path and password for the certificate):</p>
<pre>
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(&quot;\&quot;&quot;, &quot;&quot;);
// second argument - path to PFX certificate
string paramPathCertificate = args[1].Replace(&quot;\&quot;&quot;, &quot;&quot;);
// 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&lt;Uri&gt; partsToSign = new List&lt;Uri&gt;();
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(&quot;/&quot;, UriKind.RelativeOrAbsolute)));
try
{
signatureManager.Sign(partsToSign, new X509Certificate2(certificate, paramPassword));
}
catch (CryptographicException cryptographicException)
{
Console.WriteLine(&quot;Signing failed: {0}&quot;, cryptographicException.Message);
}
}
}
</pre>
<p>The usage is very simple:</p>
<pre>SignVsix.exe SampleVsixPackage.vsix certificate.pfx abc123</pre>
<p>When the file is signed, VSIX Installer shows label "Digital Signature:
&lt;Author Name&gt;" on the first page:</p>
<p><img height="354" src="../images/articles/vs-toolbox-integration/vsix-signed.png" width="450" /></p>
<h3>Dissecting the VSIX Package</h3>
<p>If you look on the project references, you can see reference to <strong>
Microsoft.VisualStudio.Shell.Immutable.10</strong>. This reference points to
Visual Studio 2010 SDK and we cannot expect this dependency present on end-user's machine. This
library contains <strong>ProvideToolboxControlAttribute</strong> class, which is
used by our <strong>ToolboxControl</strong>.</p>
<p>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 <em>any</em> control.</p>
<p>Let's take a look on the <strong>ToolboxControl.vsix</strong>. It is simply a
ZIP archive containing the manifest, resources, ToolboxControl binary and a
<strong>ToolboxControl.pkgdef</strong> 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.</p>
<h3>Update Toolbox Control via VSIX Package</h3>
<p>If you made changes to your control and want to re-install the package, an
error message appear:</p>
<p><img height="171" src="../images/articles/vs-toolbox-integration/vsix-already-installed.png" width="433" /></p>
<p>In order to provide an update, you need to increment version number in the
VSIX manifest:</p>
<p><img height="269" src="../images/articles/vs-toolbox-integration/vsix-increment-version.png" width="364" /></p>
<p>You can also increment version number in the Package class attribute, but
this is not necessary for the VSIX to perform update:</p>
<pre>
[InstalledProductRegistration(&quot;#110&quot;, &quot;#112&quot;, <strong>&quot;2.0&quot;</strong>, IconResourceID = 400)]
[Guid(GuidList.guidSampleVsixPackagePkgString)]
public sealed class SampleVsixPackage : Package
{
...
}
</pre>
<p>Of course, you can also increment version of the assembly.</p>
<h3>Uninstall the VSIX Package</h3>
<p>The VSIX Installer can be used to uninstall control from the Toolbox via
/uninstall parameter followed by package ID (the constant located in <strong>
Guids.cs</strong>: <strong>GuidList.guidSampleVsixPackagePkgString</strong>):</p>
<pre>VSIXInstaller.exe /uninstall:e3dfd099-d0ab-4b8e-b26d-639032c29ad9</pre>
<p>It is also possible to uninstall VSIX Package manually using <strong>
Extension Manager</strong> (Tools - Extension Manager...). In Visual Studio
2012/2013, the corresponding dialog is called <strong>Extensions and Updates</strong>
(Tools - Extensions and Updates...).</p>
<p><img height="244" src="../images/articles/vs-toolbox-integration/vsix-extension-manager.png" width="777" /></p>
<h3>Quiet Mode</h3>
<p>Both installation and uninstallation can be performed in quiet mode by using <strong>/quiet</strong> parameter. This will suppress
user interface of the installer, which is handy when you want to automate
Toolbox control integration with your custom installer.</p>
<h2><a name="net-versions">10. Supporting Multiple Version of .NET Framework</a></h2>
<p>Since .NET Framework is backward-compatible, building a component on lowest
possible framework ensures compatibility with higher versions as well.</p>
<p>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.</p>
<p>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.</p>
<p>The solution to this is to build several DLLs, each with different features
and possibly different target frameworks. Then integrate all the assemblies.</p>
<p>This does not pose a problem when <strong>DTE</strong> 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").</p>
<p>When <strong>TCI</strong> 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.</p>
<p>The <strong>VSI</strong> and <strong>VSIX</strong> approaches require
renaming the component or customizing Toolbox tab in the
ProvideToolboxControlAttribute (see part <em>Toolbox Control Integration using
VSIX Packages</em> for more information).</p>
<p>The <strong>VSPackage</strong> approach allows you to place all the versions
in Visual Studio Toolbox under their respective tabs.</p>
<p>When <strong>manual approach</strong> is used, you can of course add each
version of the assembly separately and also create separate tabs in the Toolbox.</p>
<h2><a name="source">11. Sample Source Code</a></h2>
<p>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.</p>
<p align="center">
<a href="../toolbox-integration.zip">Download sample source code (212 KB)</a>
</p>
<p>You can find the following folders in the archive:</p>
<p><strong>DteToolboxInstaller</strong> - 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.</p>
<p><strong>SampleControl</strong> - Windows Forms control for testing the
integration.</p>
<p><strong>SampleVsixPackage</strong> - 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.</p>
<p><strong>SampleVsPackage</strong> - VSPackage that is able to install all
control assemblies located in its own folder. Contains batch files for package
registration/unregistration.</p>
<p><strong>SignVsix</strong> - A command-line application for signing VSIX
packages. Sample batch file is provided. Valid PFX certificate need to be
provided by the user.</p>
<p><strong>TciToolboxInstaller</strong> - 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.</p>
<p><strong>VSI</strong> - Basic setting for creating VSI package and a sample
VSI package created from the files. Batch file for signing the VSI package is
provided.</p>
<p>&nbsp;</p>
<p align="center">
<a href="../toolbox-integration.zip">Download sample source code (212 KB)</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.Component Owl WinForms - .NET 2.0
</div>
</div>
<div class="right">
<div class="dextronet-feeds">
<ul class="links">
<li><a href="../feeds.rss" class="rss">Subscribe to our RSS</a></li>
<li><a href="http://twitter.com/ComponentOwl" class="twitter">Follow us on twitter</a></li>
<li><a href="https://www.facebook.com/ComponentOwl" class="facebook">Follow us on facebook</a></li>
</ul>
</div>
<div class="sidebar">
<h4>Better ListView</h4>
<a href="http://assets.componentowl.com/screenshots/15/blv-overview_original_1326499754.png?1326474554" class="screenshot" title="Better ListView control"><img alt="Better ListView" src="http://assets.componentowl.com/screenshots/15/blv-overview_thumb_1326499754.png?1326474554" /></a>
<div class="buttonz">
<a href="../betterlistview.exe" class="standard-download image-link" onclick="download_email('better-listview');">Free Download</a>
</div>
<div class="text">
</div>
</div>
</div>
</div>
<div id="screenshot-buttons" style="display: none">
<div class="buttons">
<a href="../betterlistview.exe" class="small-download image-link" onclick="download_email('better-listview');">Free Download</a>
<a href="../pricing-licensing/better-listview.html" class="small-buy image-link">See Pricing &amp; Licensing</a>
</div>
</div>
</div>
</div>
<div class="d-footer">
<div class="d-placing">
<div class="left">
<div class="latest-posts">
<h3 class="latest_from_blog"><a href="../blog.html">Latest From Our Blog</a></h3>
<h4><a href="../blog/index.html%3Fp=927.html">Activation issues and how to solve them</a></h4>
<div class="post-info">Wednesday, 01 March 2017</div>
<h4><a href="../blog/index.html%3Fp=921.html">The Three Main Advantages Better ListView has Over the Classic .NET Framework</a></h4>
<div class="post-info">Thursday, 09 February 2017</div>
<h4><a href="../blog/index.html%3Fp=914.html">BLV and Internet Explorer</a></h4>
<div class="post-info">Sunday, 13 November 2016</div>
<h4><a href="../blog/index.html%3Fp=906.html">Centering Images in Better ListView Sub-items</a></h4>
<div class="post-info">Wednesday, 06 August 2014</div>
<h4><a href="../blog/index.html%3Fp=901.html">Sub-item Check Boxes in Better ListView</a></h4>
<div class="post-info">Sunday, 06 July 2014</div>
</div>
<div class="other-posts"><a href="../blog.html">See more posts &raquo;</a></div>
</div>
<div class="right">
<div class="news">
<ul class="links">
<li><a href="../feeds.rss" class="rss">Subscribe to our RSS</a></li>
<li><a href="http://twitter.com/ComponentOwl" class="twitter">Follow us on twitter</a></li>
</ul>
<div class="news-item ">
<div class="date">27<span>May</span></div>
<div class="message">
<h4>Better ListView Express 3.15 released!</h4>
<p>
<a href="../betterlistviewexpress.exe" onclick="download_email('better-listview-express');">Download latest release</a>
</p>
</div>
</div>
<div class="news-item ">
<div class="date">27<span>May</span></div>
<div class="message">
<h4>Better ListView 3.15 released!</h4>
<p>
<a href="../betterlistview.exe" onclick="download_email('better-listview');">Download latest release</a>
</p>
</div>
</div>
<div class="news-item ">
<div class="date">27<span>May</span></div>
<div class="message">
<h4>Better SplitButton 3.15 released!</h4>
<p>
<a href="../bettersplitbutton.exe" onclick="download_email('better-splitbutton');">Download latest release</a>
</p>
</div>
</div>
<div class="news-item ">
<div class="date">27<span>May</span></div>
<div class="message">
<h4>Better Thumbnail Browser 3.15 released!</h4>
<p>
<a href="../betterthumbnailbrowser.exe" onclick="download_email('better-thumbnail-browser');">Download latest release</a>
</p>
</div>
</div>
<div class="news-item last-item">
<div class="date">08<span>Apr</span></div>
<div class="message">
<h4>Better ListView Express 3.14.0 released!</h4>
<p>
<a href="../better-listview-express/releases%3Fsince=3.14.0.html">See what's new</a>
or
<a href="../betterlistviewexpress.exe" onclick="download_email('better-listview-express');">Download latest release</a>
</p>
</div>
</div>
</div>
</div>
</div>
<div class="d-placing">
<hr />
<div class="copy"><div class="social"><a href="../support.html" class="feedback">Contact Us (Feedback)</a> <span>|</span> <a href="../feeds.rss" class="rss">RSS</a> <span>|</span> <a href="http://twitter.com/ComponentOwl" class="twitter">Twitter</a> <span>|</span> <a href="../sitemap.html" class="sitemap">Sitemap</a> <span>|</span> Our <a href="../index.html" class="stdl">task management software</a> that uses Better ListView</div>
Copyright &copy; 2018 ComponentOwl.com, Dextronet.com. All rights reserved. Read our <a href="../eula.html">EULA</a>, <a href="../disclaimer.html">Disclaimer</a> and <a href="../privacy-policy.html">Privacy Policy</a>.<br /></div>
</div>
</div>
</div>
<script src="../javascripts/app_packaged.js%3F1455269826" type="text/javascript"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-16362539-4']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script src="http://load.sumome.com/" data-sumo-site-id="854b5e00f297990012cc230023375a00787c0000216621002775a800d79ad500" async="async"></script>
<!-- Place this tag after the last plusone tag -->
<script type="text/javascript">
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/plusone.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
</body>
</html>