.NET Zone is brought to you in partnership with:

Andrey Karpov is technical manager of the OOO "Program Verification Systems" (Co Ltd) company developing the PVS-Studio tool which is a package of static code analyzers integrating into the Visual Studio development environment. Site: http://www.viva64.com/ My page on LinkedIn site: http://www.linkedin.com/pub/4/585/6a3 Andrey has posted 10 posts at DZone. View Full User Profile

How to Create, Debug and Deploy Visual Studio Extension Packages

09.18.2013
| 7902 views |
  • submit to reddit

About a year ago, we published in our blog a series of articles on the development of Visual Studio plugins in C#. We have recently revised those materials and added new sections, and now invite you to have a look at the updated version of the manual as a series of articles here on DZone.

The other articles in the series can be found here:

Creating extension packages (plug-ins) for Microsoft Visual Studio IDE appears to be quite an easy task at first glance. Excellent MSDN documentation is available, as well as various articles, examples and a lot of other sources on this topic. But, at the same time, it could also appear to be a difficult task when unexpected behavior is encountered along the way. Although it can be said that such issues are quite common to any programming task, the subject of IDE plug-in development has still not been thoroughly covered.

We developed PVS-Studio, a static code analyzer. Although the tool itself is intended for C++ developers, quite a large fragment of it is written in C#. When we had just started the development of our plug-in, Visual Studio 2005 had been considered a state-of-the-art IDE. However, with the release of Visual Studio 2012, some could say that Visual Studio 2005 is not relevant anymore, but we still provide support for this version in our tool. During our time supporting various Visual Studio versions and exploring capabilities of the environment, we've accumulated practical experience on how to correctly (and even more so incorrectly!) develop IDE plug-ins. As holding all of this knowledge inside of us was becoming unbearable, we decided to publish it here. Some of our solutions, which seem quite obvious right now, were discovered over the course of several years. The same issues could still haunt other plug-in developers.

In this series of articles, we’ll cover the following topics:

  • Basic information on creating and debugging MSVS plug-ins and maintaining these extensibility projects for several versions of Visual Studio inside a common source code base.
  • An overview of Automation Object Model and various Managed Package Framework (MPF) classes.
  • Information on extending the interface of the IDE though the automation object model's API (EnvDTE) and MPF (Managed Package Framework) classes with custom menus, toolbars, windows and options pages.
  • An overview of Visual Studio project model, Atmel Studio IDE, which is based on Visual Studio Isolated Shell, as an example of interaction with custom third-party project models.
  • Information on how to utilize the Visual C++ project model for gathering data needed to operate an external preprocessor/compiler, such as compilation arguments and settings for different platforms and configurations.

More detailed references for each of the articles in the series are available at the end of each article with the links to the MSDN library and other external resources.

The articles will cover the extension development only for Visual Studio 2005 and later versions. This limitation reflects that PVS-Studio also supports integration with Visual Studio starting only from version 8 (Visual Studio 2005). The main reason for this is that a new extensibility API model was introduced for Visual Studio 2005, and this new version is not backward compatible with previous IDE extensibility APIs.

Creating, Debugging and Deploying Extension Packages for Microsoft Visual Studio 2005/2008/2010/2012

This item contains the overview of several different methods for extending Visual Studio IDE functionality. The creation, debugging, registration and end-user deployment of Visual Studio extension packages will be explained in detail.

Creating and Debugging Visual Studio and Visual Studio Isolated Shell VSPackage Extension Modules

There exist a number of ways to extend Microsoft Visual Studio features. On the most basic level, it's possible to automate simple routine user actions using macros. An add-in plug-in module can be used for obtaining an access to an environment's UI objects, such as menu commands, windows, etc. Extension of IDE's internal editors is possible through MEF (Managed Extensibility Framework) components (starting with MSVS 2010). Finally, a plug-in of the Extension Package type (known as VSPackage) is best suited for integrating large independent components into Visual Studio. VSPackage allows combining the environment automation through Automation Object Model with the usage of Managed Package Framework classes (such as Package). In fact, while Visual Studio itself provides only the basic interface components and services, such standard modules as Visual C++ or Visual C# are themselves implemented as IDE extensions.

In its earlier versions, PVS-Studio plug-in (versions 1.xx and 2.xx to be precise, when it was still known as Viva64) existed as an add-in package. Starting with PVS-Studio 3.0, it was redesigned as a VSPackage because the functionality that the add-in was able to provide became insufficient for the tasks at hand and also the debugging process was quite inconvenient. After all, we wanted to have our own logo on the Visual Studio splash screen!

VSPackage also provides the means of extending the automation model itself by registering user-defined custom automation objects within it. Such user automation objects will become available through the same automation model to other user-created extensibility packages, providing these packages with access to your custom components. This, in turn, allows third-party developers to add the support of new programming languages and compilers through such extensions into the IDE, and also to provide interfaces for the automation of these new components as well.

Besides extending the Visual Studio environment itself, VSPackage extensions could be utilized for the addition of new features into Visual Studio Isolated\Integrated shells. Isolated\Integrated shells provide any third-party developer with the ability to re-use basic interface components and services of Visual Studio (such as a code editor, autocompletion system, etc.), but also to implement the support of other custom project models and\or compilers. Such a distribution will not include any of Microsoft’s proprietary language modules (such as Visual C++, Visual Basic and so on), and it could be installed by an end-user even if his or her system does not contain a previous Visual Studio installation.

An isolated shell application will remain a separate entity after the installation even if the system contains a previous Visual Studio installation, but an integrated shell application will be merged into the preinstalled version. In case the developer of an isolated\integrated shell extends the Visual Studio automation model by adding interfaces to his or her custom components, all other developers of VSPackage extensions will be able to handle such components as well. Atmel Studio, an IDE designed for the development of embedded systems, is an example of Visual Studio Isolated Shell application. Atmel Studio utilizes its own custom project model that, in turn, itself is the implementation of a standard Visual Studio project model for  MSBuild, and the specific version of the GCC compiler.

Projects For VSPackage Plug-in Modules: Creating the Extension Package

Let's examine the creation of the Visual Studio Package plug-in (VSPackage extension). Contrary to add-in plug-ins, developing VS extension packages requires the installation of Microsoft Visual Studio SDK for a targeted version of IDE, i.e. a separate SDK should be installed with every version of Visual Studio for which an extension is being developed. In case of the extension that targets Visual Studio Isolated\Integrated Shell, an SDK for the version of Visual Studio on which such a shell is based will be required.

We will be examining extension development for the 2005, 2008, 2009 and 2012 versions of Visual Studio and Visual Studio 2010 based Isolated Shells. Installation of Visual Studio SDK adds a standard project template for Visual Studio Package (on the 'Other Project Types -> Extensibility' page) to VS template manager. If selected, this template will generate a basic MSBuild project for an extension package, allowing several parameters to be specified beforehand, such as a programming language to be used, and the automatic generation of several stub components for generic UI elements, such as menu items, an editor, a user tool window, etc.

We will be using a C# VSPackage project (csproj), which is a project for managed dynamic-link library (DLL). The corresponding csproj MSBuild project for this managed assembly will also contain several XML nodes specific to a Visual Studio package, such as VSCT compiler and IncludeinVSIX (in later IDE versions).

The main class of an extension package should be inherited from the Microsoft.VisualStudio.Shell.Package. This base class provides managed wrappers for IDE interaction APIs, implementation of which is required from a fully functional Visual Studio extension package.

public sealed class MyPackage: Package
{
  public MyPackage ()
  {}
  ...
}

The Package class allows overriding of its base Initialize method. This method receives execution control at the moment of package initialization in the current session of the IDE.

protected override void Initialize()
{
  base.Initialize();

  ...
}

The initialization of the module will occur when it is invoked for the first time, but it also could be triggered automatically, for example after the IDE is started, or when the user enters a predefined environment UI context state.

Being aware of the package's initialization and shutdown timings is crucial. It's quite possible that the developer would be requesting some of Visual Studio’s functionality at a moment when it is still unavailable to the package. During PVS-Studio development, we've encountered several such situations when the environment "punished us" for not understanding this; for instance, we are not allowed to "straightforwardly" display message boxes after Visual Studio enters a shutdown process.

Debugging Extension Packages: Experimental Instance

The task of debugging a plug-in module or extension intended for an IDE is not a trivial one. Quite often, such an environment itself is utilized for a plug-in's development and debugging. Hooking up an unstable module to this IDE can lead to instability of the environment itself. The necessity to uninstall a module under development from the IDE before every debugging session, which in turn often requires restarting it, is also a major inconvenience (the IDE could block the DLL that needs to be replaced by a newer version for debugging).

It should be noted that a VSPackage debugging process in this aspect is substantially easier than that of an add-in package. This was one of the reasons for changing the project type of the PVS-Studio plug-in.

VSPackage solves the aforementioned development and debugging issues by utilizing the Visual Studio Experimental Instance mechanism. Such an experimental instance could be easily started by passing a special command line argument:

C:\Program Files (x86)\Microsoft Visual Studio 10.0\
  Common7\IDE\devenv.exe" /RootSuffix Exp

An experimental instance of the environment utilizes a separate independent Windows registry hive (called experimental hive) for storing all of its settings and component registration data. As such, any modifications in the IDE's settings or changes in its component registration data, which were made inside the experimental hive, will not affect the instance that is employed for the development of the module (that is your main regular instance that is used by default).

Visual Studio SDK provides a special tool for creating or resetting such experimental instances: CreateExpInstance. To create a new experimental hive, it should be executed with these arguments:

CreateExpInstance.exe /Reset /VSInstance=10.0 /RootSuffix=PVSExp

Executing this command will create a new experimental registry hive with a PVSExp suffix in its name for the 10th version of IDE (Visual Studio 2010), also resetting all of its settings to their default values in advance. The registry path for this new instance will look as follows:

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0PVSExp

While the Exp suffix is utilized by default for package debugging inside the VSPackage template project, other experimental hives with unique names could also be created by the developer at will. To start an instance of the environment for the hive we've created earlier (containing PVSExp in its name), these arguments should be used:

"C:\Program Files (x86)\Microsoft Visual Studio 10.0\
  Common7\IDE\devenv.exe" /RootSuffix PVSExp

A capacity for creating several different experimental hives on a single local workstation could be quite useful, as, for example, to provide a simultaneous and isolated development of several extension packages.

After installing the SDK package, a link is created in the Visual Studio program's menu group for resetting the default Experimental Instance for this version of the IDE (for instance, "Reset the Microsoft Visual Studio 2010 Experimental Instance").

In case of extension targeting an Isolated Shell, the issues with the "corruption" of the development environment are irrelevant, and so there is no need for Experimental Instance utilization. In any case, the more quickly you figure out how the debugging environment works, the fewer issues you'll encounter in understanding how plug-in initialization works during development.

Registering and Deploying Visual Studio Extension Packages

Registering a VS extension package requires registering a package itself, as well as registering all of the components it integrates into the IDE (for example, menu items, option pages, user windows, etc.). The registration is accomplished by creating records corresponding to these components inside the main system registry hive of Visual Studio.

All the information required for registration is placed, after building your VSPackage, inside a special pkgdef file, according to several special attributes of the main class of your package (which itself should be a subclass of the MPF 'Package' class). The pkgdef can also be created manually using the CreatePkgDef. This tool collects all of the required module registration information from these special attributes by the means of .NET reflection. Let's study these registration attributes in detail.

The PackageRegistration attribute tells the registration tool that this class is indeed a Visual Studio extension package. Only if this attribute is discovered will the tool perform its search for additional ones.

 [PackageRegistration(UseManagedResourcesOnly = true)]

The Guid attribute specifies a unique package module identifier, which will be used for creating a registry sub-key for this module in Visual Studio hive.

 [Guid("a0fcf0f3-577e-4c47-9847-5f152c16c02c")]

The InstalledProductRegistration attribute adds information to 'Visual Studio Help -> About' dialog and the loading splash screen.

 [InstalledProductRegistration("#110", "#112", "1.0", 
  IconResourceID = 400)]

The ProvideAutoLoad attribute links automatic module initialization with the activation of a specified environment UI context. When a user enters this context, the package will be automatically loaded and initialized. This is an example of setting module initialization to the opening of a solution file:

[ProvideAutoLoad("D2567162-F94F-4091-8798-A096E61B8B50")]

The GUID values for different IDE UI contexts can be found in the Microsoft.VisualStudio.VSConstants.UICONTEXT class.


The ProvideMenuResource attribute specifies the ID of a resource that contains user-created menus and commands for their registration inside the IDE.

 [ProvideMenuResource("Menus.ctmenu", 1)]

The DefaultRegistryRoot attribute specifies a path to be used for writing registration data to the system registry. Starting with Visual Studio 2010, this attribute can be dropped, as the corresponding data will be present in the manifest file of a VSIX container. Here’s an example of registering a package for Visual Studio 2008:

[DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0")]

Registration of user-created components, such as tool windows, editors, option pages, etc. also requires the inclusion of their corresponding attributes for the user's Package subclass. We will examine these attributes separately, while we will examine corresponding components individually.

It's also possible to write any user-defined registry keys (and values to already existing keys) during package registration through custom user registration attributes. Such attributes can be created by inheriting the RegistrationAttribute abstract class.

[AttributeUsage(AttributeTargets.Class, Inherited = true,
  AllowMultiple = false)]
    public class CustomRegistrationAttribute : RegistrationAttribute
    {
    }

The RegistrationAttribute-derived attribute must override its Register and Unregister methods, which are used to modify registration information in the system registry.

The RegPkg tool can be used for writing registration data to the Windows registry. It will add all of the keys from the pkgdef file passed to it into the registry hive specified by the /root argument. For instance, the RegPkg is utilized by default in the Visual Studio VSPackage project template for registering the module in the Visual Studio experimental hive, providing convenient, seamless debugging of the package being developed. After all of the registration information has been added to the registry, Visual Studio (devenv.exe) should be started with the '/setup' switch to complete registration for new components inside the IDE.

Deploying Plug-ins for Developers and End-users: Package Load Key

Before proceeding to describe the deployment process itself, one particular rule should be stressed:

Each time a new version of the distribution containing your plug-in is created, this new distribution should be tested on a system without Visual Studio SDK installed to make sure that it will be registered correctly on the end-user system.

Today, as the releases of early versions of PVS-Studio are past us, we do not experience these kinds of issues, but several of the early versions were prone to them.

Deploying a package for Visual Studio 2005/2008 will require launching the regpkg tool for a pkgdef file and passing the path to the Visual Studio main registry hive into it. Alternately, all keys from a pkgdef can be written to the Windows registry manually. Here is an example of automatically writing all the registration data from a pkgdef file by regpkg tool (in a single line):

RegPkg.exe /root:Software\Microsoft\VisualStudio\9.0Exp
  "/pkgdeffile:obj\Debug\PVS-Studio-vs2008.pkgdef"
  "C:\MyPackage\MyPackage.dll"

After adding the registration information to the system registry, it is necessary to start Visual Studio with a /setup switch to complete the component registration. It is usually the last step in the installation procedure of a new plug-in.

Devenv.exe /setup

Starting the environment with this switch instructs Visual Studio to absorb resource metadata for user-created components from all available extension packages so that these components will be correctly displayed by the IDE's interface. Starting devenv with this key will not open its main GUI window.

We do not employ the RepPkg utility as part of PVS-Studio deployment; instead we manually write required data to the registry by using our stand-alone installer. We chose this method because we have no desire to be dependent on external third-party tools, and we want full control over the installation process. Still, we do use RegPkg during plug-in development for convenient debugging.

VSIX Packages 

Beginning from Visual Studio 2010, the VSPackage deployment process can be significantly simplified through the usage of VSIX packages. A VSIX package itself is a common (Open Packaging Conventions) archive containing a plug-in's binary files and all of the other auxiliary files that are necessary for the plug-in's deployment. By passing such an archive to the standard VSIXInstaller.exe utility, its contents will be automatically registered in the IDE:

VSIXInstaller.exe MyPackage.vsix

VSIX installer could also be used with the /uninstall switch to remove the previously installed package from a system. A unique GUID of the extension package should be used to identify such a package:

VSIXInstaller.exe /uninstall: 009084B1-6271-4621-A893-6D72F2B67A4D

Contents of a VSIX container are defined through the special vsixmanifest file, which should be added to a plug-in's project. A vsixmanifest file permits the following properties to be defined for an extension:

  • Targeted Visual Studio versions and editions, which will be supported by the plug-in.
  • A unique GUID identifier.
  • A list of components to be registered (VSPackage, MEF components, toolbox control etc.).
  • General information about the plug-in to be installed (description, license, version, etc.).

To include additional files into a VSIX container, the IncludeInVSIX node should be added to the declarations inside your MSBuild project. Alternately, they could also be marked as included into VSIX from their respective property windows by opening it from Visual Studio Solution Explorer.

<Content Include="MyPackage.pdb">
  <IncludeInVSIX>true</IncludeInVSIX>
</Content>

In fact, the VSIX file could be viewed as an almost full-fledged installer for extension packages on the latest versions of Visual Studio (2010 and 2012), allowing the extensions to be deployed by a "one-click" method. Publishing your VSIX container in the official Visual Studio Gallery for extensions allows end-users to install such packages through the Tools -> Extension Manager IDE dialog.

VSIX allows the extension to be deployed either for one of the regular Visual Studio editions, or for the isolated\integrated shell based distributions. In case you’re developing an extension for an isolated shell application, instead of the Visual Studio version, the VSIX manifest file should contain a special identification string for the targeted environment. For example, the identification string for Atmel Studio 6.1 should be "AtmelStudio, 6.1." But, if the extension you are developing utilizes only common automation model interfaces (such as the ones for the text editor, abstract project tree and so on), and does not require any of the specific ones (for example, interfaces for Visual C++ projects), then it is possible for you to specify several different editions of Visual Studio, as well as isolated shell based ones, in the manifest file. This, in turn, will permit you to use a single installer for a wide range of Visual Studio-based applications.

This new VSIX installation procedure in Visual Studio 2010 substantially alleviates package deployment for end-users (as well as for developers, themselves). Some developers even had decided to support only VS2010 IDE and versions above it, if only not to get involved with the development of a package and installer for earlier IDE versions.

Unfortunately, several issues can be encountered when using the VSIX installer together with the Visual Studio 2010 extension manager interface. For instance, sometimes the extension's binary files are not removed correctly after uninstall, which in turn blocks the VSIX installer from installing/reinstalling the same extension. As such, we advise you not to depend upon the VSIX installer entirely and to provide some backup, for example by directly removing your files from a previous plug-in installation before proceeding with a new one.

Package Load Key 

Each VSPackage module loaded into Visual Studio must possess a unique Package Load Key (PLK). The PLK key is specified through the ProvideLoadKey attribute for the Package subclass in 2005/2008 versions of the IDE.

[ProvideLoadKey("Standard", "9.99", "MyPackage", "My Company", 100)]

Starting with Visual Studio 2010, the presence in a package of a PLK, as well as of the ProvideLoadKey attribute, is not required, but it can still be specified in case the module under development is targeting several versions of MSVS. The PLK can be obtained by registering at the Visual Studio Industry Partner portal, meaning it guarantees that the development environment can load only packages certified by Microsoft.

However, systems with Visual Studio SDK installed are exceptions to this, as Developer License Key is installed together with the SDK. It allows the corresponding IDE to load any extension package, regardless of the validity of its PLK.

Considering the aforementioned, it is necessary to stress the importance of testing the distribution on a system without Visual Studio SDK present, because the extension package will operate properly on a developer's workstation regardless of its PLK correctness.

Extension Registration Specifics in the Context of Supporting Several Different Versions of Visual Studio IDE 

By default, the VSPackage project template will generate an extensibility project for the version of Visual Studio that is used for the development. This is not a mandatory requirement, though, so it is possible to develop an extension for a particular version of IDE using a different one. It also should be noted that after automatically upgrading a project file to a newer version through the devenv /Upgrade switch, the targeted version of the IDE and its corresponding managed API libraries will remain unchanged, i.e., from a previous version of Visual Studio.

To change the target of the extension to another version of Visual Studio (or to register an extension to this version, to be more precise), you should alter values passed to the DefaultRegistryRoot attribute (only for 2005/2008 IDE versions, as starting from Visual Studio 2010 this attribute is no longer required) or change the target version in the VSIX manifest file (for versions above 2008).

VSIX support appears only starting from Visual Studio 2010, so building and debugging the plug-in targeted for the earlier IDE version from within Visual Studio 2010 (and later) requires setting up all the aforementioned registration steps manually, without VSIX manifest. While changing a target IDE version, one should also not forget to switch referenced managed assemblies, which contain COM interface wrappers utilized by the plug-in, to the corresponding versions as well.

Altering the IDE target version of the plug-in affects the following Package subclass attributes:

  • The InstalledProductRegistration attribute does not support overloading of its constructor with a (Boolean, String, String, String) signature, starting from Visual Studio 2010.
  • The presence of DefaultRegistryRoot and ProvideLoadKey attributes is not mandatory starting from Visual Studio 2010, as similar values are now specified inside VSIX manifest.

References
1.   MSDN. Experimental Build.
2.   MSDN. How to: Register a VSPackage.
3.   MSDN. VSIX Deployment.
4.   MSDN. How to: Obtain a PLK for a VSPackage.
5.   MZ-Tools. Resources about Visual Studio .NET extensibility.
6.   MSDN. Creating Add-ins and Wizards.
7.   MSDN. Using a Custom Registration Attribute to Register an Extension.
8.   MSDN. Shell (Integrated or Isolated).



Published at DZone with permission of its author, Andrey Karpov.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Violet Giraffe replied on Sat, 2013/09/28 - 3:57pm

The part 2 link refers to this very page, so that part of the guide is inaccessible. Can you please fix it? Thanks for the guide, it's very helpful!

Oliver Hauch replied on Tue, 2014/02/18 - 4:18am in response to: Violet Giraffe

 Though it's quite a while since you asked for the link, i just tried a bit and found this working link for part 2

http://dzone.com/articles/using-visual-studio-automation


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.