El TecnoBaúl de Kiquenet

Kiquenet boring stories

Troubleshooting VSIX

Posted by kiquenet en 11 agosto 2014

VSIX Best Practices

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 breaksbinary 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.

How VSIX extensions are discovered and loaded in VS 2010

is the new technology used for deploying extensions in Visual Studio 2010. The primary goal of this new technology is to encourage extension creation and consumption by easing the management (“management” meaning Browsing/Installing/Uninstalling/Enabling/Disabling) of Visual Studio extensions. To take full advantage of the VSIX installer for deploying your extensions, it helps to know a little bit more about how Visual Studio decides which extensions to load.

Dmitry Goncharenko provided a good high-level overview of how these VSIX extensions are discovered and loaded in his post Bootstrapping of VS packages and VSIX extensions in VS2010. Let’s now take a more in-depth look at how this process works.

An extension consists of:

  1. an extension.vsixmanifest file, which contains metadata about the extension
  2. any additional files that represent the extension content. This could include MEF assemblies, VS Package assemblies, VS Template ZIP files, PkgDef files, etc..

The simplest vsix extension would contain only the extension.vsixmanifest file, though that would admittedly be a very uninteresting extension.

Extension Discovery

In accordance with the goal of simplicity, a VSIX extension install consists of only copying the extension files into one of a few well-known locations. These locations are defined in the “Master PkgDef” file, located at <VsInstallRootFolder>\Common7\IDE\devenv.pkgdef and pictured below.

MasterPkgDef

The relevant values are described below.

Variable

Description

ApplicationExtensionsFolder

The root folder under which machine wide VSIXs are deployed.

(This is set as <VsInstallRootFolder>\Common7\IDE\Extensions)

UserExtensionsRootFolder

The root folder under which user specific VSIXs are deployed.

(This is set as %LocalAppData%\Microsoft\VisualStudio\10.0\Extensions)

PkgDefSearchPath

A list of additional folders where extensions will be searched for. The name for this property was originally defined by the PkgDef subsystem. This list is shared with vsix extensions so that any extensions that contain .pkgdef files will be properly loaded. Note that this includes ApplicationExtensionsFolder.

Upon initialization, the Extension Manager service, SVsExtensionManager, will search the above locations for extension.vsixmanifest files. The PkgDefSearchPath folders are searched first, followed by the UserExtensionsRootFolder.

Extension Loading Rules

At this point, each extension must pass a few trials before being considered installed by the Extension Manager. Before diving into the details, here’s a quick summary of these:

  • The extension.vsixmanifest XML conforms to the XSD.
  • The extension has not been marked for deletion.
  • The extension’s identifier cannot conflict with any other previously discovered extensions.

First, if the extension.vsixmanifest XML does not conform to the VSIX manifest XSD schema, it is ignored. If the manifest XML passes schema validation, then it will be deserialized into an object model in memory. Second, the Extension Manager needs to verify that the extension is not marked for deletion. Before going any further, it would be helpful to discuss what exactly this means.

If you’ve uninstalled an extension through the Extension Manager dialog in Visual Studio, you may have noticed that the uninstall occurs extremely fast. This is because the extension is only marked for deletion at that time. On a subsequent initialization of the Extension Manager (the next time Visual Studio or the VSIX Installer are launched), all of the pending deletions are cleaned up on a background thread *after* all installed extensions have been discovered. Therefore, any extension that is marked for deletion should be discarded and no longer considered installed.

Third, if the extension contains the same ID as another extension that has already been discovered, it will be discarded. This is where the search order becomes important. The UserExtensionsRootFolder is searched last in order to give precedence to machine wide extensions when an ID conflict is encountered.

Once an extension has passed these checks, it is considered “installed” by the Extension Manager service. Figuring out whether an extension is not installed because of one of the above reasons is easy since the Extension Manager logs this information to the Visual Studio activity log. The activity log can be enabled by running the Visual Studio process (<VsInstallRootFolder>\Common7\IDE\devenv.exe) with the ‘/log’ switch, as follows:

devenv.exe /log <path_to_log_file>

The Mechanics of Enabled/Disabled Extensions

So your extension is successfully “installed”, but how does the Extension Manager determine whether it’s “Enabled”? The answer depends upon where your extension is installed. Extensions installed to any directory in the PkgDefSearchPath list of folders are always enabled. Extensions installed to the UserExtensionsRootFolder path must be individually enabled through a list maintained in the HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\ExtensionManager\EnabledExtensions registry key. TheExtension Manager Install API, used by the extension install dialog as well as the VSIX installer, will write the entry in the EnabledExtensions key for any newly installed extension so that extensions installed in this manner are automatically enabled. You’ll notice that if you manually install an extension by copying the extension files to a subfolder in the UserExtensionsRootFolder path, it will be disabled at first because the corresponding entry in EnabledExtensions has not been added. Enabling the extension can also be accomplished through the Extension Manager dialog.

Enable

Lastly, extensions in the UserExtensionsRootFolder path will all be disabled when running Visual Studio as an administrator, if the following option is not checked under Tools->Options->Environment->Extension Manager

Options

This option was likely the most common culprit for why users (particularly administrators running WindowsXP) found that their extensions were not enabled in Beta2 and prior builds of Visual Studio, since the default value for the option was ‘False’. Due to the frequency with which customers ran into this problem, we’ve changed the default value for this option to ‘True’. Note that the Extension Manager dialog provides a warning if you’re running Visual Studio elevated, but do not have this option checked.

AdminMessage

When in doubt about why an extension does not seem to be loaded or enabled, you should consult the Visual Studio activity log, which will output various diagnostics during the loading process. You may also want to check out the following posts related to extension loading:


References:

http://blogs.msdn.com/b/visualstudio/archive/2010/06/09/vsix-best-practices.aspx?Redirected=true

http://blogs.msdn.com/b/visualstudio/archive/2010/02/19/how-vsix-extensions-are-discovered-and-loaded-in-vs-2010.aspx

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: