Category Archives: VS Packages

MSDN Magazine article: Creating Extensions for Multiple Visual Studio Versions

Back in April or May I started to write a post on this blog about creating extensions for multiple Visual Studio versions. I have been struggling to support multiple versions of Visual Studio with my MZ-Tools add-in since Visual Studio .NET (2002) / 2003, and other IDEs such as VB6/VB5/VBA before that. So, I know how tricky it can be to share as much code as possible between versions, and using a single binary dll / setup if possible. It seems that I am not the only one wanting to target multiple Visual Studio versions with a single vsix package because I have seen lots of questions in the forums, chat rooms and private email asking about it. The post on this blog was never finished, because it was becoming so long that I thought it would be… a good article for MSDN Magazine! And finally today it has been published in the August issue:

Visual Studio – Creating Extensions for Multiple Visual Studio Versions

Building a VSIX extension with the Visual Studio 2017 Build Tools

As I explained in the post Migrating the build of a VSIX project to a build server if you are a solo developer, I am taking the steps to build my MZ-Tools extension on a build/release server. As part of that process, I realized than rather than installing Visual Studio 2017 Community edition on the server, I could use the Visual Studio 2017 Build Tools that were thought, well, for build servers that don’t need the overhead of a Visual Studio 2017 installation. They are a lightweight version of Visual Studio 2017 without the IDE (devenv.exe executable). They can be used to build either managed (C#, VB.NET, etc.) projects or native (C++) projects. Incidentally my MZ-Tools solution has both type of projects.

The Visual 2017 Build Tools can be downloaded from here. Once you install them on a clean machine, you will notice that they provide only the following:

  • A built-in (non-optional) set of components to build MSBuild-based projects (for example managed projects).
  • An optional workload “Visual C++ build tools”.
  • An optional workload “Web development build tools”.

There are also optional individual components to install .NET Framework 4.6.1 support Windows SDKs, ATL support, etc.:

In my case my extension needs to use .NET Framework 2.0 for some projects (I still support Visual Studio 2005). Since that version is not installed by default on modern versions of the Windows OS, I need to install it going to “Control Panel”, “Programs and Features” item, “Turn Windows Features on or off” link:

My obfuscator tool needs the .NET Framework 3.5 SDK (or the .NET Framework 2.0 SDK). While the Visual Studio 2017 Community installer provides the optional individual component “.NET Framework 3.5 developments tools”, the installer of Build Tools 2017 doesn’t. That is not only a pity but also causes a bug if you install yourself the Windows 7.0 SDK that contains the .NET Framework 3.5 SDK that I reported here: the resource .resx files of a .NET project targeting .NET Framework 2.0 are compiled using the Assembly Linker (al.exe tool) of the .NET Framework 4.0, which will cause them to fail silently at run-time. Microsoft fixed the bug just in time for RTM in Visual Studio 2017, but the Build Tools 2017 still has the bug due to the lack of the “.NET Framework 3.5 developments tools”. There is a fix that I explained in the bug report if you find this problem. I have also requested to Microsoft to include the “.NET Framework 3.5 developments tools” in the installer of Build Tools 2017.

My extension for Visual Studio targets version 2012, so I need to stick to .NET Framework 4.5, not some higher version. Since that version is not provided by the Build Tools 2017, I need to install the Windows 8.0 SDK, that contains the .NET Framework 4.5 assemblies and SDK.

For the C++ projects, I needed to install:

  • “Visual C++ ATL Support”: required to get files such as atlbase.h, etc.
  • “Windows 8.1 SDK”: I could upgrade to some Windows 10 SDK version but they occupy much more space on disk.
  • “UCRT SDK”: the Universal Common RunTime that provides files such errno.h and other files in the folder “C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt”.
  • “.NET Framework 4.6.1 SDK”: to get files such as mscoree.h / mscoree.lib in the folder “C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\”.

My build script uses tf.exe to set a workspace and download the latest sources. Alas, tf.exe is not installed with Build Tools 2017 (Visual Studio 2017 installs the Team Explorer extension, that contains that file in the folder “C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer”). Now there is a standalone installer for Team Explorer (that includes tf.exe) announced here. Since that’s a bit overkill, I just copied tf.exe and the required dlls.

The build script needs also nuget.exe, but that’s easy to get.

So, when I thought that I had all the required components installed, I tried to build the extension. I got the following problems:

First, the error “Error’MSB4226: The imported project “(…)\VSSDK\Microsoft.VsSDK.targets” was not found.” Initially I thought it was a bug, that I reported to Microsoft here, but I discovered that the problem was solved setting the “VisualStudioVersion” MSBuild property, something that a machine with the full Visual Studio 2017 does and that a machine with the Build Tools 2017 does if you open a developer command prompt. Since I was not using it, I passed it as a parameter to the MSBuild script. It can be defined too inside the .csproj file, something that previous Visual Studio versions did automatically but recent versions don’t.

Then I got an error about a missing Microsoft.VisualStudio.Settings.15.0 file. How is that a file from Visual Studio is required by the NuGet package that provides the Visual Studio SDK?. It happens that Microsoft.VsSDK.Build.Tasks.dll, the file that contains the MSBuild tasks needed when creating a VSIX file, references it. But on a machine without Visual Studio, that dll is not present. I discovered that despite the error, the VSIX file was generated correctly, so that DLL was required for a task after generating the VSIX file. That task is to deploy the generated VSIX file to the experimental instance for debugging. Since on a release server that step is not needed, I knew how to instruct the .csproj project file to avoid it:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
   ... 
   <!-- Do not deploy the extension in Release configuration -->
   <DeployExtension>False</DeployExtension>
</PropertyGroup>

Finally, when I thought that all obstacles were solved, I got an error about the Microsoft.Visualstudio.Shell.Interop.dll being delay-signed rather than strong-signed. Since my development machine has tons of Visual Studio versions and SDKs, I discovered that all the Microsoft.Visualstudio.Shell.Interop.dll files were strong-named except an old one, which somehow Microsoft shipped delay-signed, and that was the one that I was referencing. So, I only need to change it by the correct version. And finally, my extension generates a VSIX file on a release server with only the Build Tools 2017 plus some additional components, but without Visual Studio 2017 installed.

The next step is to install some agent to connect to the Build/Release Management of Visual Studio Team Services.

Migrating the build of a VSIX project to a build server if you are a solo developer

Some months ago I started a long and slow journey to migrate the build process of my MZ-Tools extension from a custom .NET-based builder that ran on my development computer to Visual Studio Team Services, leveraging its Build management and Release management capabilities. My goals are to learn those capabilities and, well, to use them as if I were a team. I haven’t reached yet that destination but I have made significant progress and I am quite close now. In the process I have realized somewhat ashamed that I wasn’t following the best practices in a lot of places. I say “somewhat” because there are some mitigating circumstances:

  • Being a solo developer it is too easy to arrange things in such a way that only works on your development machine. You don’t have a team to warn you that it doesn’t work outside your development machine.
  • Even if you change your development machine to a new one from time to time, or use two development computers, you use the same username and tend to install and configure the software in the same way.
  • My Visual Studio solution and some projects were born in the year 2002, with the first Visual Studio .NET. At that time Team Foundation Server, NuGet, MSBuild, etc. didn’t even exist on paper.
  • I have all the Visual Studio versions and VS SDKs from 2005 to 2017 installed on my development machine. That causes that you don’t think carefully where a DLL is referenced from.
  • I have also all the .NET Frameworks and SDKs from .NET Framework 2.0 installed on my development machine. Another source for undocumented hidden dependencies.
  • My Visual Studio solution has become quite complex over the years with several projects and technologies:
    • One project that uses .NET Framework 2.0 and C# for the core plug-in, that is reused at binary level for many Microsoft’s IDEs.
    • Four projects with host adapters for Visual Studio (2005, 2008, 2010) as add-in, VBA (Microsoft Office 2000 or higher, even on Windows XP), VB 6.0 and VB 5.0, using .NET Framework 2.0, C# and 3rd party tool for the setups.
    • One project with a host adapter for Visual Studio (2012, 2013, 2015 and 2017) as package, using .NET Framework 4.5, C#, and VSIX.
    • Four projects with COM Shims for VBA (32-bit and 64-bit), VB 6.0 and VB 5.0 using Visual C++, ATL, Windows 8.1 SDK and the .NET CLR loader APIs.
    • One project for a portable version for VBA, using .NET Framework 2.0 and C#.
    • Help file and online help generated by a 3rd party tool.
    • Two projects with integration tests.
    • Obfuscation performed by a 3rd party tool, that requires delay signing.

Microsoft tends to create a new VSIX with each new Visual Studio release but most of us want to create a single package and single VSIX for as many Visual Studio versions as possible, using always the latest Visual Studio version for development (Visual Studio 2017 at the time of this writing). If this is your case, even if your solution is not as complex as mine, ask yourself these questions:

In the process that I have followed first I envisioned the final result:

  • I would use the hosted agent of the Build management of Visual Studio Team Services to provide the following benefits:
    • Gated check-ins to prevent code that breaks the build.
    • Gated check-ins to prevent code that violates code analysis rules.
    • Integration tests. This will require a major effort because I use my own test runner and testing framework instead of Visual Studio Test with the MSTest framework.
  • I would use a private agent on the Release server with the Release management of Visual Studio Team Services to provide the following benefits:
    • Tracking of releases deployed to the test environment, to the pre-production environment and to the production environment.
    • Maybe automated releases.

For these purposes I decided that I would use a new “Build” configuration of the solution for the Build server. In this configuration the obfuscation, help file and online help, setups, etc. are not created. For the Release server I would use the “Release” solution configuration that performs all those additional steps.

For the Build server it is quite easy and I can use the hosted agent of Visual Studio Team Services since I don’t need any 3rd party tools.

For the Release server I cannot use the hosted agent because I need the 3rd party tools that I use to obfuscate, create the help, setups, etc. In the process I have also removed the need for admin rights that my custom builder required previously (the hosted agent of Visual Studio Team Services doesn’t allow admin rights either).

The milestones would be:

  • Migrate the custom .NET-based builder to a MSBuild script. This took me weeks but it’s done.
  • The solution and projects, when built in “Release” configuration, would auto-increment version numbers and would obfuscate the output assemblies, build the help, setups, etc. with their own MSBuild targets without requiring external steps.
  • A master MSBuild script would perform some additional steps before building the solution (such as creating a workspace, getting latest files, restoring NuGet packages, etc.) and some other steps afterwards (such as publishing and archiving binaries).
  • Build on a separate workspace on my development machine. Previously I was building on the same workspace used for development. I know, I know, a horrible bad practice.
  • Create a separate temporary virtual machine to act as Release server with only Visual Studio 2017 Community edition installed, without previous Visual Studio versions to identify missing dependencies and build with the master MSBuild script.
  • Discard the virtual machine of the previous step and create a new virtual machine with the Build Tools 2017 but without Visual Studio 2017, identify and install missing dependencies, build with the master MSBuild script and document everything. I have reached this point!
  • Install a private Visual Studio Team Services agent on the virtual machine that acts as Release server and launch the master build script from the Release management section of Visual Studio Team Services.
  • Create additional scripts to publish binaries to the pre-production and production environment rather than using manual FTP.
  • Repeat everything with the solution for my ASP.NET web site.

I encourage you to follow the same journey if you are a solo developer (and of course if you are a team). At the very least, the exercise of building on a separate build server with only Build Tools 2017 will unhide you hardcoded paths, references that are not provided by source control or supplied as NuGet packages, etc.

Microsoft now working on conditional payloads for VSIX. And for VS 2017 service release!.

Almost three years ago I created this request on Uservoice:

Conditions support for <Content> section of VSIX manifest

This request tries to solve a problem that is increasingly tricky: while Microsoft (or developers that work within Microsoft) release a new extension for each new Visual Studio version, most of us want desperately to update our existing extensions to support multiple versions of Visual Studio. Ideally we would like to have a single package DLL that targets many Visual Studio versions, but if you want to use the latest extensibility APIs, then most of the time you are forced to add references that demand a new package DLL because those references don’t exist on old versions of Visual Studio. But yet, we would like to ship those different package DLLs in the same unified VSIX file, so that for some versions of Visual Studio the VSIX file deploys a package DLL, and for other versions of Visual Studio the VSIX file deploys a different package DLL. Even you may want to deploy different .pkgdef files, as I explained in the request. And nowadays, with the release of Visual Studio 2017 and its manifest “v3”, we would like even different manifests (“v1” for Visual Studio 2010, “v3” for Visual Studio 2012-2017) within the same VSIX, which would solve the most common question on the forums these days: “How can I make a single VSIX that targets Visual Studio 2010-2017?” (sorry, you can’t).

Today, I have received an update from UserVoice indicating that Microsoft is now working on it, and that it will be “for a future servicing release of Visual Studio 2017”. Big news!!!

You can still vote for the idea to show user demand 🙂

SAMPLE: How to create a Visual Studio context menu

For the sake of completeness, I have added another sample to the list of samples about creating top menus, submenus, toolbars, etc. that I created a few days ago on GitHub:

HOWTO: Create a Visual Studio context menu
Demonstrates how to create a context menu that is shown when right-clicking on a toolwindow. To show the toolwindow click the View > Other Windows > My toolwindow command.

contextmenu

contextmenudark

That is, in this case we don’t want to add a menu entry to an existing context menu, but we want to create a whole context menu with entries to be shown when right-clicking on something, typically a control of a modal dialog or a toolwindow that belongs to our package.

There are a couple of ways to accomplish this:

The first one is using automation (EnvDTE): you create by code a commandbar that you show using the CommandBar.ShowPopup method. This was the only approach for add-ins and you have a sample in the article HOWTO: Create a context menu using a Visual Studio commandbar popup from an add-in. Since packages can use DTE, this approach is suitable. The commandbar can even have menu entries without an underlying command. But this approach is not the focus of this post.

The second one, only for packages, is using a .vsct file. The Menu element of the .vsct file can be of several kinds, and one of them is “Context”. Then you add commands to it through a group, like any other type of menu. But how is shown?. You need to use either the IOleComponentUIManager.ShowContextMenu method or the IVsUIShell.ShowContextMenu method. I have used the latter method, which receives several parameters such as the Guid of the command set, the Id of the context menu (both defined in the .vsct file) and the point where to show it.

The sample also shows the correct approach to initialize the usercontrol of a toolwindow (to pass it the package). Take into account that a toolwindow in Visual Studio can be shown in two cases:

  1. Clicking the command that shows the toolwindow.
  2. Automatically when Visual Studio is launched if the toolwindow was shown when Visual Studio was closed the last time. Visual Studio is smart enough to know the package owner of the toolwindow to show, then it loads the package and shows the toolwindow.

The initialization of the usercontrol cannot be done in the constructor of the toolwindow when the usercontrol is created because at that point the base.Package is null in the case 2:

public MyToolWindow() : base(null)
{
   this.Caption = "My toolwindow";

   m_toolWindowControl = new ToolWindowControl();

   this.Content = m_toolWindowControl;
}

Instead, you need to override the OnToolWindowCreated method since at this point the base.Package is already initialized:

public override void OnToolWindowCreated()
{
   VSPackageContextMenu package;

   base.OnToolWindowCreated();

   package = (VSPackageContextMenu)base.Package;

   m_toolWindowControl.Initialize(package);
}

The strange case of System.NotImplementedException “The method or operation is not implemented.” using SQL Server database projects

Yesterday I got a bug report from a user of my MZ-Tools package for Visual Studio 2015:

System.NotImplementedException “The method or operation is not implemented.”

The exception happened calling the EnvDTE.ProjectItem.IsOpen Property (viewKind) method when using SQL Server Database Projects provided by SQL Server Data Tools (SSDT):

databaseproject

As you may know, SQL Server Database projects are quite good automation-friendly projects, that is, they implement EnvDTE interfaces (EnvDTE.Project, EnvDTE.ProjectItem, etc.) so you don’t need to use the low level IVsHierarchy, etc. interfaces of Visual Studio. But somehow, they missed to implement something somewhere.

I tried to reproduce the problem on my side to no avail. My extension worked happily with the .sql files (EnvDTE.ProjectItem) of that kind of project. So, which kind of EnvDTE.ProjectItem didn’t implement the IsOpen property? The user sent me a helpful clue: he was using a database reference. I was not aware of that thing, but certainly in SQL Server database projects you can add references to databases:

databasereference

And as soon as I added a reference to the “master” database, I could reproduce the problem. So, my extension was treating the ProjectItem nodes below the References node as files that could be “opened”, and the ProjectItem.IsOpen method was not implemented (which is certainly right). But, how is that the nodes below the References node were being processed for database projects (which is incorrect) while they were not processed for regular (C#, VB.NET) projects?. The reason is that for regular projects, the EnvDTE.Project.ProjectItems collection of the project doesn’t return the References node, which is correct. For database projects, the EnvDTE.Project.ProjectItems collection does return the References node, which is a bug. I wanted to verify that the internal implementation of database projects has this bug so I used .NET Reflector to see the internals of the Microsoft.VisualStudio.Data.Tools.Package.dll package and I found that there is a ReferenceContainerNode type that has a GetAutomationObject that returns an OAReferenceFolderItem instance:

getautomationobject

There shouldn’t be a folder for references in the automation model (EnvDTE). The automation model provides the VSLangProj.References collection to handle the references of a project. I won’t report the bug because there is an almost zero chance of Microsoft fixing it, and anyway I needed to find a fix for the current version of the SQL Server Data Tools, which took me to the next problem: how can an extension identify reliably the “References” ProjectItem?.

I could check if the Name property of the ProjectItem is “References”, but that approach is always prone to errors due to localization (in Spanish would be “Referencias”). The SQL Server Data Tools doesn’t seem to be localized, but I wanted to investigate another approaches. It happens that the IVsHierarchy of the References node implements an interface named IReferenceContainer. So, one could cast that node to IReferenceContainer and if the cast is successful then it’s the References node. Alas, that interface is declared internally in database projects, and in MPF.Project.NonShipping.dll for other projects. At the end, I opted for getting the EnvDTE.ProjectItem.Object property, convert it to a string, and compare it to “Microsoft.VisualStudio.Data.Tools.Package.Internal.OAReferences”, which is as bad as comparing the Name to “References”, but anyway, it works for now.

SAMPLES: How to create top menus, sub menus, context menus, toolbars

The questions of how to create a toolbar, a top menu, a sub menu, an entry on a context menu, etc. appear from time to time on the forums, so I have created a bunch of samples on GitHub:

visualstudioextensibility/VSX-Samples

They use what I consider best practice: the use of the CommandPlacements section in the .vsct file for centralized place to set parent-child relationships.

The code samples are the following:

HOWTO: Create a Visual Studio toolbar
Demonstrates how to create a toolbar.

toolbar

HOWTO: Create a Visual Studio top menu
Demonstrates how to create a top menu.

topmenu

HOWTO: Create Visual Studio commands on a submenu
Demonstrates the use of the CommandPlacements section of the .vsct file to place a command inside a new group on a submenu inside a new group on the Solution context menu and on the Standard toolbar.

submenutoolbar

submenucontextmenu

HOWTO: Create a Visual Studio command on the Solution context menu
Demonstrates the use of the CommandPlacements section of the .vsct file to place a command inside a new group inside the Solution context menu.

solutioncontextmenu

HOWTO: Create a Visual Studio command on the project context menu
Demonstrates the use of the CommandPlacements section of the .vsct file to place a command inside a new group on the context menu of a Windows project, Web project, or multiple projects selected.

projectcontextmenu

HOWTO: Create a Visual Studio command on the file context menu
Demonstrates the use of the CommandPlacements section of the .vsct file to place a command inside a new group on the context menu of a file or multiple files (belonging to the same project, or to different projects).

filecontextmenu

HOWTO: Create a Visual Studio command on the code window context menu
Demonstrates the use of the CommandPlacements section of the .vsct file to place a command inside a new group on the code window context menu.

codewindowcontextmenu

SAMPLE: How to create a Visual Studio command that accepts parameters

Some weeks ago a user of my MZ-Tools add-in asked me how to automate the Review Quality feature. So far, this feature could be invoked on demand, or automatically when an output file is built using the user interface. The request was very reasonable: automated reviews when the code is built using some script, or when code is checked-in in source control (not in the context of a build). So, the requirement is to execute some feature programmatically with input parameters (feature name, log file, etc.) and generate output files (results file, log file, etc.). My last attempt at automating some feature of MZ-Tools was in 2007, when I wrote the post Frustrations with command-line add-ins for Visual Studio. So, it was time for another attempt.

If you try to execute a command with arguments, for example in the Command window, likely you will get this error:

commandwindow

It happens that there are four steps that you need to code to ensure that your command accepts parameters:

commandwithparametes

Rather than listing the steps here, I have created a sample on GitHub:

HOWTO: Create a Visual Studio command that accepts arguments

One of the steps (the magical “$” value that must be assigned to the ParametersDescription property of the OleMenuCommand) is so esoteric that it’s not properly documented anywhere, but some comments of Ryan Molden in a forum explains something about it.

The four versions of a Visual Studio package

One of the confusing aspects when you create a new Visual Studio package is that there seems to be many places where a version number appears. They are the following:

  1. The version on the Tools > Extensions and Updates dialog:

VersionExtensionsAndUpdates

2. The version on the Help > About dialog:

VersionHelpAbout

3. The version on the deployment folder:

VersionFolder

4. The version of the file:

VersionFile

5. The version of the assembly:

VersionAssembly

All those five versions are set in four places, that are the following:

1. The version on the Tools > Extensions and Updates dialog and the version on the deployment folder are the same version and it is set in the source.extension.vsixmanifest file, Version field. It is recommended (in my opinion) to set it to the file version (4 numbers):

VersionManifest

2. The version on the Help > About dialog is set in the package class, InstalledProductRegistration attribute. It is recommended to set the same value than in the previous point, so that the Help > About and the Tools > Extensions and Updates dialogs show the same value:

VersionPackageClass

3. The version of the file is set in the AssemblyInfo.cs file, AssemblyFileVersion attribute. This file version is a Win32 concept (not a .NET concept) and it is best practice to change it on each build:

FileVersionAssemblyInfo

4. The version of the assembly is set in the AssemblyInfo.cs file, AssemblyVersion attribute. This assembly version is a .NET concept and it is not required to change it on each build:

AssemblyVersionAssemblyInfo

Note: to learn the difference between Assembly File Version and Assembly version read the Knowledge Base Community article How to use Assembly Version and Assembly File Version.

Resources to create high-DPI images with the new Visual Studio 2015 Image Service

In my post Visual Studio extensions and DPI-awareness I introduced the basics that you need to know to become DPI-aware. The final step once all the user interface of your extension handles high-DPI gracefully is to provide high-DPI images for the toolbars, as Visual Studio 2015 does since CTP 3. Apparently, support for high-DPI images through a new image service was in the Visual Studio 2015 RTM release, but it hasn’t been until a couple of days ago that Microsoft has published the MSDN documentation. Here you have the resources to learn and use it:

UPDATE (Jan 26, 2016): The Visual Studio Image Library, which contains .zip files with VS images, has been updated with VS 2015 images.