2011-04-23

VSX extensions/packages published and installed through the visual studio gallery will automatically check for updates and allow you to install the newest versions.

If you’re not deploying your extension through the gallery, or just want auto-update capability for yourself, you can mimic this process yourself using the IVSExtensionManager interface. But when I wanted implement this, exactly how to do it was not documented anywhere.

The process is actually straight-forward:

You’ll need to reference:

Microsoft.VisualStudio.ExtensionManager.dll
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.Interop.8.0

Warning, the extension assembly isn’t published in the .NET components, you’ll have to manually browse to the GAC to reference it: C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.VisualStudio.ExtensionManager\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.ExtensionManager.dll

var manager = Package.GetGlobalService(typeof(SVsExtensionManager)) as IVsExtensionManager;

Using the manager, get a list of installed extensions

var names = new string[] { "Ganji", "Ganji.History", "Ganji.Margin" };
var myExtensions = manager.GetInstalledExtensions().Where(ext =>names.Contains(ext.Header.Name)).ToList();

Next, we want to be able to download and then install a new version of our extension. This is the general logic. The details follow.

bool needsRestart = false;
foreach (var extension in myExtensions)
{
    var newVersion = FetchIfUpdated(extension, webRepository.Where(r => r.Name == extension.Header.Name).SingleOrDefault());
    if (newVersion != null)
    {
        Install(manager, extension, newVersion);
        needsRestart = true;
    }
}
// Client needs to restart
return needsRestart;

You can check and fetch a new version with the IVsExtensionRepository manager.

private IInstallableExtension FetchIfUpdated( IInstalledExtension extension, RepositoryEntry entry)
{ 
    var version = extension.Header.Version;
    var repoManager = Package.GetGlobalService(typeof(SVsExtensionRepository)) as IVsExtensionRepository;
    try
    {
        var newestVersion = repoManager.Download(entry);
        if (newestVersion.Header.Version > extension.Header.Version)
        {
            return newestVersion;
        }
    }
    catch (Exception ex)
    {
        // may not have internet connection, etc...
        Console.WriteLine(ex.Message);
    }
    return null;
}

But you have to define your own IRepositoryEntry, which will be used as a parameter in the Download call.

class RepositoryEntry : IRepositoryEntry
{
    public string Name { get; set; }
    public string DownloadUrl { get; set; }
    public string VsixReferences { get; set; }
}

Finally, installing the extension involves disabling your old extension, uninstalling it, installing the new extension, then enabling it. A restart will be required before it takes affect however.

private RestartReason Install( IVsExtensionManager manager, IInstalledExtension currentExtention, IInstallableExtension updatedExtension)
{
    // Uninstall old
    manager.Disable(currentExtention);
    manager.Uninstall(currentExtention);

    // Install newer version
    var restartReason = manager.Install(updatedExtension, false);

    // Check newly installed version and enable (which is not the case by default)
    var newlyInstalledVersion = manager.GetInstalledExtension(updatedExtension.Header.Identifier);
    if (newlyInstalledVersion != null)
    {
        manager.Enable(newlyInstalledVersion);

        Console.WriteLine(string.Format("Updated: from {0} to {1} at {2}",
           currentExtention.Header.Version,
           newlyInstalledVersion.Header.Version,
           newlyInstalledVersion.InstallPath));
    }
        
    return restartReason;
}


blog comments powered by Disqus