VSIX Best Practices

VSIX Best Practices

Rate This
  • Comments 7

This post is about a new way to install extensions to Visual Studio, introduced in VS 2010, called the VSIX file. The information it contains will be of most interest to readers who develop Visual Studio extensions, but I encourage users who download and install those extensions to read it as well.

A VSIX file conforms to the ECMA Open Packaging Conventions (OPC) standard. It’s created as part of a VSIX project build in Visual Studio, and you can view its contents with any zip file utility. If you upload your VSIX to the Visual Studio Gallery, your customer can install it right in Visual Studio, in the new Extension Manager:

image

It can also be installed by downloading and double clicking on the file, and uninstalled either in the Extension Manager, or by simply deleting the associated files. You can find introductory information about VSIX here and here.

The VSIX feature comes with a lot of options. In most cases you don’t have to understand them all. This post is a list of tips that will give you some guidance about how to use the new VSIX capabilities in the best way. Here’s what I’m going to talk about:

  • How to package your extension into a VSIX in the simplest way
  • How to install via MSI if you need to
  • How to use VSIX versioning
  • What to avoid

Packaging your extensions using VSIX 

  • Use strong names for all your assemblies. You don’t want your “util.dll” to collide with somebody else’s; if you don’t use strong names the system won’t distinguish between them, and somebody will get a run time error.
  • Distribute your whole product in one independent VSIX if you can. The feature does allow one VSIX to depend on another. But, save that for situations where each one is developed and shipped separately, because shipping a single VSIX will reduce the amount of information you have to understand.
  • If you ship more than one VSIX, and they share common assemblies, copy the common assemblies into each separate VSIX. This has the effect of shipping your whole product in one VSIX described above. There’s no runtime harm in shipping copies of common assemblies – in memory the CLR will only load one:
    image
    But be aware that it will load only one, and the first one loaded wins. So you should ship updates to all your VSIXs that contain common assemblies together.
  • If your product is extending another extension, then your VSIX needs to take a dependency on the target VSIX using either the Select Installed Extension or Manual Reference choice in the dialog below (which you raise by clicking the Add Reference button in the VSIX Manifest Editor).
    image
    image 
    In this case
    • Read the Versioning section below to understand the best way to specify the version of your target VSIX in this dialog.
    • Stay aware of your target extension’s updates, and test to make sure your extension is still compatible with each update.
  • If your VSIX publishes an API that another VSIX will use
    • Read the versioning section below to understand how your users will expect your versioning to behave.
    • Maintain binary compatibility between versions if you can; this just makes life simpler for your extenders.
    • Keep your extenders aware of approaching updates so they can test against them. Make sure they know in advance if you plan to release a version that breaks compatibility.

Installing via MSI

Some extensions still need to be installed by MSI: for example some of your files might have to be in a specific, well-known location, you might have a component like an MSBuild task that VSIX install doesn’t support, you might need to use binding redirection – see more information here. There’s no problem with doing that. In fact, we provide a way for an MSI installed extension to make itself visible in the new Extension Manager, so that the customer can see all his extensions in one place.

To make your extension visible in the Extension Manager, your MSI install should create a subdirectory in the Extensions directory for the hosting product:

For a non-administrative, per-user install (recommended) in Visual Studio, the path will look like this:
        Users\user id\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\your company\extension name\version\   
and for a per-machine install:
        Program Files\VS 10.0 install directory\Common7\Ide\Extensions\your company\your extension name\version\
An Isolated Shell application will define its own Extension directory.

In that folder, put the extension.vsixmanifest file built by your VSIX project, with an added element that marks it as installed by MSI:

 <Identifier Id="VSIXProject2.Microsoft IT.8532242f-afdc-44fa-82b2-0b6b5afc1c38">
    <Name>VSIXProject2</Name>
<InstalledByMsi>true</InstalledByMsi>


</Identifier>

Note that although the user will then see the extension in the Extension Manager, since it’s installed by MSI, he still needs to manage it through Windows Add/Remove Programs.

Versioning

If your extension is self-contained (i.e. you distribute it in a single VSIX that doesn’t have any dependencies on other ones), and no other VSIXs will depend on yours (i.e. your VSIX doesn’t expose any APIs), you don’t need to read this section. If your VSIX does offer or consume APIs, or you distribute multiple VSIXs with common shared assemblies, read on for more information.

First, let’s do a quick review of how versioning works in the CLR. For an assembly with a strong name, its CLR identity comes from a combination of the file name on disk, Assembly Version string, an optional cultural attribute, and a digital signature. When one assembly references (i.e. consumes APIs from) another one, the consumer is targeted to a particular version of the referenced assembly at build time. (Binding redirection can change this at run time, but the VSIX installer doesn’t support that yet.) The version string contains four segments: <major version>.<minor version>.<build number>.<revision> (for example “1.2.123.0”). The recommended convention for using the version string is that when an assembly’s API breaks binary compatibility, the major version is incremented. (Note that I’m talking about Assembly Version, which is part of the strong name, not Assembly File Version, which is purely informational – you can use Assembly File Version any way you like. See more information here.)

That’s all background information. Now let’s talk about using versioning with VSIX files. The first thing I’m going to recommend, although it probably sounds a little unexpected, is that you not change the Assembly Version strings when you ship an update of your VSIX. This is because, as I mentioned above, the VSIX installer doesn’t support binding redirection yet, so if you do change any segment of an assembly version number, you may break downstream VSIXs that depend on the old version number of your assembly. The VSIX file has its own mechanism for version management, and I recommend that you use that one instead, because its added flexibility gets you around the binding redirection issue.

For a VSIX that uses an API from another VSIX:

The syntax of a VSIX version string is the same as the assembly one, and we will use the recommended convention to indicate that a new release breaks binary compatibility with the old one: incrementing the major version number. The big advantage of the VSIX versioning mechanism is that if you’re consuming an API from another VSIX, you can specify a range of version numbers of the target VSIX that you’re compatible with. Let’s see how this works. When you raise the Add VSIX Reference dialog in the VSIX Manifest Editor:

image

 image

in the Version fields just above, you can specify a range between minimum and maximum version numbers that you’re compatible with. If the developer of the VSIX whose API you consume obeys the versioning conventions, you can specify a range like from Min 1.0 to Max 1.9999 to indicate that you will use any version of your dependency between those two. When the VSIX you depend on installs, for example, version 1.2, you will be compatible with it. When the user attempts to install 2.0, the installer will recognize the incompatibility:

image

and display a warning dialog:

image

If the user updates the extension you depend on anyway, your extension will be disabled because of the incompatibility, and he should look for an update from you that’s compatible with the new version of the API.

If for any reason you believe you are dependent on a specific version of the target extension, you can code that number as the Min and Max values to target only that version:

image

If you only code the Min value, you will bind to anything equal or higher. I don’t recommend that, because binary compatibility breakage (in the example below, from 1.x to 3.0) can lead to run time errors.

image

For a VSIX that offers an API to other VSIXs:

If you release an extension that offers an API, you should handle the versioning at the VSIX level. This means leaving the Assembly Version string unchanged across releases, and incrementing your VSIX version number (shown in the VSIX manifest editor below).

image

It’s great for your consumers if your API can maintain binary compatibility across releases. If you need to break compatibility, increment the major version number. But at that point, when the VSIX installer upgrades your extension, all the consumers of your API will have to ship releases that are compatible with the new API.

Things to avoid:

    • Embedding one VSIX inside another (using the Add payload selection in the Add VSIX Reference dialog). 
      image
      In the current version of Visual Studio, certain combinations of embedded VSIXs and version updates don’t install properly. This feature should be avoided for now; the safest practice is to install VSIXs that have a dependency relationship separately.

Summary

In this article we looked at a set of recommendations for using the new VSIX feature. What I want to leave you with is: minimize complexity. Take advantage of the new VSIX features as you need them, but keep your life as simple as possible by using only the features you need. That way the VSIX install experience will be simple for your customers, which is the  reason VSIX was invented.

Please feel free to post comments and questions!

Gary Horen
Program Manager, Visual Studio Extensibility

Leave a Comment
  • Please add 5 and 6 and type the answer here:
  • Post
  • Please, VSIX authors, please do NOT bundle many different features as a single extension. Instead, let each feature install as a separate extension and use the dependency features highlighted above so that users can turn one features on and off as separate extensions. The AllMargins extension is a great example of this. The various "Power Commands" and "Pro Power Tools" (unfortunately from Microsoft) extensions are horrible examples.

  • @efalskn --

    Better support for dependencies, and finer-grained control over what is and is not installed/enabled are definitely things we have in mind for future enhancements to the VSIX feature. My advice to bundle everything into one VSIX is for now, when the user experience installing dependent VSIXs is not what we'd like it to be. The reason I'm suggesting that you try to distribute a single VSIX in this release is to avoid making the user have to understand how dependencies work. I know this is not ideal. It would be great to hear more about what scenarios you think are most important, as we plan for the next version.

    Thanks for your feedback.

    -Gary Horen

  • I agree with efalsken. If the user can't understand how dependencies work then what are they doing installing extensions in a product like visual studio?

  • I couldn't find a more appropriate place for this question. I actually obtained the source code for the AllMargins extension (posted somewhere on one of MS's old code sharing sites)--which I LOVE! I'm in the process of converting it so that it will work with VS2012. The conversion has gone well except for one last thing and I'm hoping that someone can point me in the right direction.

    This extension (written by a MS employee) violates the "best practices" spelled out here, as mentioned by @efalsken by nesting multiple VSIX packages. This extension has a somewhat deep hierarchy of packages, each package having a hierarchy of assembly reference dependencies. For example: CaretMargin.dll -> OverviewMarginImpl.dll -> OverviewMargin.dll -> SettingsStoreImp.dll -> SettingStore.dll (I've shortened the DLL names).

    The crucial part is that the SettingsStore.dll project contains a SettingsStore.pkgdef file that contains the following:

    [$RootKey$\BindingPaths\{GUID}]

    "$ProjectPath$"=""

    When the extension is loaded (say by VS 2010), a registry entry is made under HKCU\Software\Microsoft\Visual Studio\10.0_Config\BindingPaths: a key of {GUID} is created and a string value of the expansion of $ProjectPath$ is also created. However, after modifying the extension manifests appropriately, Visual Studio 2012 successfully installs the extension, but the BindingPaths are not created as they were in Visual Studio 2010. In fact, no binding paths at all are created.

    Is there any reason why this behavior is occurring? How can I track down what's preventing these registry entries from being created when the extension is installed (or after VS starts and loads the extension)? All I get right now is that, for example, FileNotFoundException, probing failed for SettingsStore.dll (shortened DLL name, but I think you get the picture). Probing failed because no binding paths are specified and VS2012 can't find the assembly.

  • Quick update: I tried installing the single VSIX extension in question (in this case, SettingsStore.dll -- again, shortened DLL name) that contains the *.pgkdef file with the specification of a binding path.

    VSIXInstaller reported that the extension installed successfully into Visual Studio 2012 Pro, but again, no registry entry was made under HKCU\Software\Microsoft\Visual Studio\11.0_Config\BindingPaths despite the presence of the pkgdef file (after having restarted Visual Studio 2012).

    Any ideas?

  • @fourpastmidnight:

    Please run devenv /setup after your extension install. That should instantiate the entries in HKCU

    Matt Kaufman

    Visual Studio IDE Services Program Management

  • Actually, I just figured this out today. It is not necessary to run devenv /setup anymore. In fact, this  wasn't even necessary on Visual Studio 2010. I did not have a reference to Visual Studio MPF in the VSIX manifest (I'm not sure if this is strictly required, but it's part of the default VSIX template that ships with the VSSDK). Of more importance, however, is the fact that the sub-extensions that have *.pkgdef files did not have a VsPackage element in the Content element whose inner text is the name of the *.pkgdef file. In Visual Studio 2010, apparently this was not required in order for the *.pkgdef file to be processed. It appears that this is now required for Visual Studio 2012 to process the *.pkgdef file.

    Thanks for the suggestion, however.

Page 1 of 1 (7 items)