More hell issues code signing VSIX extensions

Continuing on the subject of “Code signing a VSIX package with a hardware-based USB token key”, there are some issues with the new hardware-based code signing certificates shipping since June 1, 2023 make the code signing extremely tricky or impossible in Yubikey keys, the most popular ones.

Here they are and please vote up for them for Microsoft to improve the current scenario:

  1. “Misleading documentation regarding SHA256 required to sign VSIX packages”: https://developercommunity.visualstudio.com/t/Misleading-documentation-regarding-SHA25/10481764
  2. “Insufficient documentation to sign VSIX packages”: https://developercommunity.visualstudio.com/t/Insufficient-documentation-to-sign-VSIX/10481776
  3. “Add support in Visual Studio 2015 and higher for ECDSA-SHA256 (and not only RSA-SHA256) for code-signing VSIX packages”: https://developercommunity.visualstudio.com/t/Add-support-in-Visual-Studio-2015-and-hi/10481797
  4. “VSIXInstaller should log the exception that causes “Invalid signature” installing code-signed VSIX extensions”: https://developercommunity.visualstudio.com/t/VSIXInstaller-should-log-the-exception-t/10481800
  5. “vsixsigntool.exe shows incorrect error message when duplicating the /fd flag”: https://developercommunity.visualstudio.com/t/vsixsigntoolexe-shows-incorrect-error-m/10481804

Visual Studio Extensibility is tricky, as usual 🙂 And current tooling / documentation / implementation doesn’t help. Even the tool that you use for code signing (vsixsigntool.exe) has a verify verb (apart from the sign verb that you know) that is useless because, being a native tool (not a .NET-based tool), it can state that the signature is valid but VSIXInstaller.exe (which is .NET-based) can state that it is not (for example, using ECDSA-SHA256) because crypto support is not exactly the same in native implementation and in .NET implementation. It’s a good thing that code signing is not mandatory for extensions as it is for regular setups…

And although not related to code signing, you may also vote up the following, which would be very handy for VSIX developers and are long overdue:

  1. “Single VSIX entry in Visual Studio marketplace for VS 2022 and for VS 2019 and lower”: https://developercommunity.visualstudio.com/t/Single-VSIX-entry-in-Visual-Studio-marke/1584630
  2. “VSIX project with SDK-style csproj”: https://developercommunity.visualstudio.com/t/VSIX-project-with-SDK-style-csproj/1572145
  3. “Make Shared Project SDK style”: https://developercommunity.visualstudio.com/t/Make-Shared-Project-SDK-style/10296159



Code signing a VSIX package with a hardware-based USB token key

Yes, I know that 5 years have passed since my last post here. That last post was about Code signing a VSIX package targeting multiple Visual Studio versions and this one is a continuation. As you may know, there are three kinds of code signing certificates:

  • Individual Validation (IV)
  • Organization Validation (OV)
  • Extended Validation (EV)

Until June 1, 2023, only EV certificates were issued on hardware-based devices such as USB keys. But since that date, IV and OV certificates provided by all vendors are only issued either on Federal Information Processing Standard 140-2 (FIPS 140-2) USB tokens or some kind of cloud-based code signing FIPS-compliant service. This change is in compliance with the Certificate Authority/Browser (CA/B) Forum’s new key storage requirements to increase security for code signing keys. Although it is expected that instances of code signing keys being stolen and misused by malicious actors will be greatly reduced, this poses a challenge to sign VSIX packages using the vsixsigntool.exe tool due to lack of documentation.

In this scenario, you have:

  • A certificate without the private key. You have got this certificate from the vendor and imported into the USB key, or you have got a USB device with the certificate already preloaded from the vendor directly. When you insert the key in the USB port (with maybe a Windows smart card driver installed) the certificate appears also in the Windows Certificate Store for user (certmgr.exe), in the Personal > Certificates node. Apparently the vsixsigntool.exe tool doesn’t support certificate files in .per or .crt formats, and the .pfx format requires the private key, so you have to use the .p7b format for the certificate file. You can export the certificate from the Windows Certificate Store to the .p7b format.
  • A private key inside the USB key that cannot leave the device (hence the enhanced security).

Since you don’t have a password-protected .pfx file with the certificate and the private key, you can’t use the /p flag (for the password) of the vsixsigntool.exe tool. Instead, you must provide access to the private key inside the USB device using the following flags:

  • /f: Path to the certificate file in .p7b format (without the private key)
  • /csp : Specify the Cryptographic Service Provider (CSP) containing the Key Container of the private key.
  • /k : Specify the name of the Key Container of the private key.

The steps to sign VXIS packages with a hardware-based USB key are the following:

  • Get the SHA1 Thumbprint of the certificate. To get it, locate the certificate Windows certificate store of user (certmgr), open it and in the Details tab look for the Thumbprint property. It should be a string of 40 characters.
  • Get the name of the CSP provider containing the container of the private key. To get it use this command and use the “Provider” field of the output:

    certutil.exe -user -store my "<SHA1-thumbprint-of-your-certificate>"

    It should be something like “Microsoft Smart Card Key Storage Provider”.
  • Get the name of the Key Container of the private key. To get it use the same previous command and use the “Key Container” field of the output. It should be something like a GUID.

With all those pieces, the command would be something like this:

vsixsigntool.exe sign /f <CodeSigningCertificate.p7b> /csp <CertificateCryptographicServiceProvider> /k <CertificateKeyContainerName> /sha1 <CertificateSHA1Thumbprint> /fd sha256 /t <TimeStampServerUrl> <MyVSIXProject.vsix>

At that point you should get a prompt to enter the PIN of your USB key to get access to the private key and the VSIX file should be signed successfully.

Code signing a VSIX package targeting multiple Visual Studio versions

You know that I am such a big fan of targeting multiple Visual Studio versions with the same VSIX file that I wrote an article for MSDN Magazine explaining the approach to target  from Visual Studio 2012 to Visual Studio 2017. In that article I didn’t explain how to code sign such VSIX file to work with multiple Visual Studio versions but this week I have got a (personal) code signing certificate.

First things first, if you don’t code sign your VSIX file, the VSIX installer shows that the digital signature is none:

To code sign a vsix file, you can’t use the regular signtool.exe tool of the Windows SDK. You need to use the vsixsigntool.exe that you can get from NuGet:

And the official documentation to sign VSIX packages is here:

Signing VSIX Packages

In the samples that follow, I will assume that you have the vsixsigntool.exe file and a code signing certificate named CodeSigningCertificate.pfx in the same bin\debug folder that contains a vsix file named MyVSIXProject.vsix.

If you specify in the source.extension.vsixmanifest file that your VSIX package only targets Visual Studio 2012 (11.0) and 2013 (12.0):

 <Installation>
 <InstallationTarget Id="Microsoft.VisualStudio.Professional" Version="[11.0,12.0]" />
 </Installation>

and you sign the VSIX file with with the /fd sha1 option to use the SHA1 algorithm (do not confuse with the /sha1 option, which is used to select the correct certificate):

vsixsigntool.exe sign /f CodeSigningCertificate.pfx /sha1 "<sha1 bytes>" /p MyPassword /fd sha1 MyVSIXProject.vsix

then, although initially you can get this “invalid certificate” error:

after a few attempts while connected  to internet then it works as expected (I don’t know yet why the initial error, does anyone know?):

However, if you sign the VSIX file with with the /fd sha256 option to use the recommendable SHA256 algorithm:

vsixsigntool.exe sign /f CodeSigningCertificate.pfx /sha1 "<sha1 bytes>" /p MyPassword /fd sha256 MyVSIXProject.vsix

then you get an error because the algorithm is not supported:

Now, if you specify in the source.extension.vsixmanifest file that your VSIX package only targets Visual Studio 2015 (14.0) and 2017 (15.0):

 <Installation>
 <InstallationTarget Id="Microsoft.VisualStudio.Professional" Version="[14.0,15.0]" />
 </Installation>

and you sign the assembly with with the /fd sha1 option to use the SHA1 algorithm:

vsixsigntool.exe sign /f CodeSigningCertificate.pfx /sha1 "<sha1 bytes>" /p MyPassword /fd sha1 MyVSIXProject.vsix

then the VSIX installer shows “Invalid signature”:

That is because Visual Studio 2015 and higher no longer support the SHA1 algorithm, only the SHA256 algorithm. If you sign the VSIX file with with the /fd sha256 option to use the SHA256 algorithm:

vsixsigntool.exe sign /f CodeSigningCertificate.pfx /sha1 "<sha1 bytes>" /p MyPassword /fd sha256 MyVSIXProject.vsix

then the VSIX installer shows the correct signature:

But, what if you specify in the source.extension.vsixmanifest file that your VSIX package targets from Visual Studio 2012 (11.0) to Visual Studio 2017 (15.0)?:

 <Installation>
 <InstallationTarget Id="Microsoft.VisualStudio.Professional" Version="[11.0,15.0]" />
 </Installation>

Of course, using the SHA256 algorithm would case the “invalid signature” warning when installing the VSIX package for Visual Studio 2012 or 2013. But if you use the SHA1 algorithm:

vsixsigntool.exe sign /f CodeSigningCertificate.pfx /sha1 "<sha1 bytes>" /p MyPassword /fd sha1 MyVSIXProject.vsix

then, because the VSIX installer notices that the VSIX package is also valid for Visual Studio 2012 and 2013, it no longer shows “Invalid signature” for Visual Studio 2015 and 2017:

Note: according to Ed Dore in this post, this approach wouldn’t work with older versions of the VSIX Installer if the manifest states explicitly the higher version:

 <Installation>
 <InstallationTarget Id="Microsoft.VisualStudio.Professional" Version="[11.0,15.0]" />
 </Installation>

and the workaround was not to specify the higher version:

 <Installation>
 <InstallationTarget Id="Microsoft.VisualStudio.Professional" Version="[11.0,)" />
 </Installation>

But that bug is already fixed and you can specify the higher version.

The VisualStudio-TestHost project to execute interactive UI tests in Visual Studio

I started with automated tests for my MZ-Tools extension early in the development of version 7.0 (then an add-in, not a package), ten years ago, around the year 2008 or so. At that time Visual Studio 2008 provided a Visual Studio TestHost dll (even with source code, if I remember correctly) to run automated tests of Visual Studio packages using the own Visual Studio. I remember that it was so painful to use that approach (crashes, hangings, etc.) that after months of investment I threw all the stuff and started to build my own test framework infrastructure and test runner:

(yep, I have 3,354 automated integration tests for my extension for Visual Studio)

It was perfect in most aspects because I owned the code and could adapt it to my needs but it had one important inconvenient: it didn’t allow Continuous Integration (CI). Instead, I ran the tests manually before each (monthly) build. It had another inconvenient: I learned automated testing by myself without formal training and I ended with tons of integration tests (which use the real Visual Studio to host the extension), no system tests (that would mock Visual Studio) and no unit tests (that just test a single method or class). I know now that, for performance reasons, it is much better to have have a pyramid with tons of unit tests, lots of system tests and few integration tests.

Last year I started to focus on DevOps (even if I am a solo developer) and continuous integration, and decided to make my test framework infrastructure compatible with VS Test / MSTest, so I could keep using my own test runner for integration tests but use them 100% as system tests mocking Visual Studio with stubs:

As you can see, my progress is quite modest at this point (only 133 system tests vs 3,354 integration tests) because I need to mock the full EnvDTE.FileCodeModel for most tests. But it is a feasible approach because I have fully mocked with stubs the whole VBA / VB6 IDEs that I use for MZ-Tools 8.0 for VBA / VB6. So, for example I have 1,822 integration tests for VB6 with my own test runner (an add-in for VB6):

And the same 1,822 tests as system tests using the test runner of Visual Studio:

Once I finish the system tests for Visual Studio, the next step is to give another try to run the integration tests using the test runner of Visual Studio, rather than my own test runner, which will allow me Continuous Integration not only for system tests, but also for integration tests. For such approach, I learned in the Two videos about building High Performance Extensions by Omer Raviv about the Microsoft/VisualStudio-TestHost project on GitHub. I have still to read and learn about it, but I think it is based on the code of the VS TestHost of 2008 and hopefully these 10 years have been used to solve the problems that caused me to abandon that approach.

Update (July 29, 2018): yesterday I finished the last stubs for Visual Studio and now all the integration tests for the real Visual Studio are also system tests with stubs that simulate the behavior of Visual Studio:

Two videos about building High Performance Extensions by Omer Raviv

Omer Raviv, author of the OzCode extension for Visual Studio, has recorded two videos with Robert Green on Channel 9 about building high performance extensions for Visual Studio. If creating extensions for Visual Studio is already tricky, creating high performance extensions is quite difficult and Omer has some advices and techniques about it.

The first video is this one:

Building High Performance Extensions Part 1

And the second one is this:

Building High Performance Extensions Part 2

BUG: EnvDTE.CodeElement.GetStartPoint(vsCMPartBody) / GetEndPoint(vsCMPartBody) throw COMException for expression-bodied methods and properties

Some days ago I got a bug report from a user that was using the new expression-bodied methods and properties introduced by C# 6.0, which have the following form:

public class Class1
{
   public int Function1() => 0;
   public int Property1 => 0;
}

After some investigation, it turned out that the problem was in the calls to get the start/end points of the body of the EnvDTE.CodeElement representing them:

codeElement.GetStartPoint(vsCMPartBody)
codeElement.GetEndPoint(vsCMPartBody)

For those kind of code elements, the code model throws COMException (not even NotImplementedException), when it should return valid values since those expression-bodied members have…well, a body.

Today I have reported the bug on the GitHub project for Roslyn, since Microsoft changed the implementation of the automation code model (EnvDTE.Project.CodeModel, EnvDTE.ProjectItem.FileCodeModel) for Visual Studio 2015 to be based on the underlying .NET Compiler Platform (“Roslyn”).

EnvDTE.CodeElement.GetStartPoint(vsCMPartBody)/GetEndPoint(vsCMPartBody) throw COMException for expression-bodied methods and properties

This implementation change caused some bugs in Visual Studio 2015 betas that fortunately were fixed after I reported them, and some calls to GetStartPoint / GetEndPoint are not implemented as I reported two years ago:

EnvDTE.CodeElement.GetStartPoint(part)/GetEndPoint(part) not implemented for many parts

As a workaround for this bug, I assumed that the expression-bodied member expands to a single line, and I searched the string “=>” to find the start of the body.

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

About the new privateregistry.bin file of Visual Studio 2017

As I explained in the post Some implications of the new modular setup of Visual Studio 2017 for VSX developers, Visual Studio 2017 introduces among others two significant changes compared to Visual Studio 2015:

  • It allows several Visual Studio editions (Community, Professional, Enterprise) to coexist at the same time on the same machine. For VSX developers, this means that Visual Studio 2017 installations now use different folders on disk, and instance Ids.
  • It uses its own private registry. This post is about this.

From Visual Studio .NET 2002 to Visual Studio 2008, Visual Studio used two registry keys:

  • HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\<version>: this per-machine entry was created when Visual Studio was installed (which required admin rights) and 3rd party extensions could add registry entries to it (to register packages or DDEX providers, etc.) because it was never deleted (otherwise Visual Studio would become unusable and would require reinstallation).
  • HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version>: this per-user entry was created the first time Visual Studio was launched for a user account, and contained per-user settings. It was never deleted (otherwise user settings would be lost).

This worked fine for a few years, until Microsoft wanted Visual Studio not to require admin rights to run or even to install 3rd party extensions, and to allow file-based registration of such extensions using .pkgdef files. So, a third registry key was introduced:

  • HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version>_Config
that per-user entry was created by Visual Studio when it deemed it convenient merging two sources:
  1. The per-machine Registry configuration, HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\<version>
  2. Per-user and per-machine .pkgdef files on disk.

So, from time to time, Visual Studio 2010 copied to a per-user configuration the per-machine configuration (source #1) along with per-machine and per-user .pkgdef files on disk (source #2). This process happened when Visual Studio was launched for the first time on a user account, after a change of configuration, etc. And for this to work, at run-time the devenv.exe process redirects  HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\<version> to HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version>_Config.

All that means that 3rd party setups of extensions shouldn’t write directly to the HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version>_Config key, because it could be deleted and created again at any time. Instead, setups should either write directly to HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\<version> (which requires admin rights), to HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version> (which doesn’t require admin rights), or, better yet, avoid completely writing to the registry and use instead .pkgdef files on disk, either on per-machine folders (which requires admin rights) or on per-user folders (which doesn’t require admin rights).

So far so good, but now Visual Studio 2017 stops using those three registry keys and it uses its own private registry. This is a file named privateregistry.bin which is located in the (hidden) folder C:\Users\<user>\AppData\Local\Microsoft\VisualStudio\<version>_<instance-id>, where <version> is 15.0 and <instance-id> is a random value determined when each Visual Studio 2017 edition is installed:

If you want to use regedit.exe to load that private registry, follow the steps detailed in How to examine Visual Studio 2017 registry. You will end seeing this:

At this point you should be aware of all this in Visual Studio 2017:

  • The private registry (privateregistry.bin file) is per-user, not per-machine. As such, it is not created when Visual Studio is installed, but when Visual Studio is launched for the first time for a user account.
  • The private registry provides two keys: 15.0_<instance-id> and 15.0_<instance-id>_Config.
  • The key 15.0_<instance-id> is equivalent to HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version> in previous versions. As such, it is never deleted (otherwise a user would lose her per-user configuration).
  • The key 15.0_<instance-id>_Config is equivalent to HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\<version>_Config in previous versions. As such, it can be deleted and recreated by Visual Studio when it needs to update the configuration.
  • There is no per-machine private registry. So, there is no equivalent of HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\15.0 (well, such key exists, but it stores minimal settings). The installation of Visual Studio 2017 uses exclusively per-machine .pkgdef files instead that are scanned when Visual Studio is loaded for the first time for a user account to create the key 15.0_<instance-id>_Config in the per-user private registry.
  • At run-time, the devenv.exe process of Visual Studio 2017 redirects registry operations on Visual Studio keys to the privateregistry.bin file. So, this change is transparent for extensions (DLLs) that run in the devenv.exe process. Setups (which are external processes) are not so lucky. While they could use the RegLoadAppKey function to write to the 15.0_<instance-id> key for per-user extensions (never for 15.0_<instance-id>_Config, which would be overwritten later), it is much better to switch to .pkgdef files on disk (either per-machine, or per-user). If you really need to mess with the Visual Studio setup, then see Microsoft/vs-setup-samples.

Sample code and utilities to get installed Visual Studio 2017 editions programmatically

As I explained in the post Some implications of the new modular setup of Visual Studio 2017 for VSX developers, Visual Studio 2017 has changed all that you knew about installations of Visual Studio. In this episode of Channel 9, Art Leonard explains to Robert Green the internals of this re-architecture of Visual Studio:

The use of a private registry file causes that if you want to know programmatically the installed editions of Visual Studio 2017, the old approaches don’t work. For example, my article HOWTO: Detect installed Visual Studio editions, packages or service packs is now obsolete.

Fortunately, Microsoft provides a new Setup API to query the installed editions of Visual Studio 2017 or the highest VSIXInstaller.exe, along with sample code and utilities:

Visual Studio Setup Configuration Samples
Microsoft/vs-setup-samples
“This is a sample in various programming languages that demonstrates how developers can use the new Visual Studio setup query API. The included samples show how to use the new setup configuration API for discovering instances of Visual Studio 2017”.

Visual Studio Locator
Microsoft/vswhere
“Over the years Visual Studio could be discovered using registry keys, but with recent changes to the deployment and extensibility models a new method is needed to discover possibly more than once installed instance. These changes facilitate a smaller, faster default install complimented by on-demand install of other workloads and components. vswhere is designed to be a redistributable, single-file executable that can be used in build or deployment scripts to find where Visual Studio – or other products in the Visual Studio family – is located.”

Visual Studio Setup PowerShell Module
Microsoft/vssetup.powershell
“This PowerShell module contains cmdlets to query instances of Visual Studio 2017 and newer. It also serves as a more useful sample of using the Setup Configuration APIs than the previously published samples though those also have samples using VB and VC++.”

VSIX Installer Bootstrapper
Microsoft/vsixbootstrapper
“An installer that can be chained with other packages to locate the latest VSIXInstaller.exe to use for installing VSIX extensions. One of the great new features of Visual Studio 2017 is an initial smaller and fast install. To compliment a smaller – but powerful – initial feature set, installing additional workloads and components on-demand is supported for both end users and package developers. Package developers can install their VSIX extensions for Visual Studio using this bootstrapper to find the latest version of VSIXInstaller.exe and install their extension(s). This may be preferable for extensions that support Visual Studio 2017 or newer than installing extensions in Windows Installer .msi packages, since MSIs cannot run concurrently in separate processes. Other deployments may also benefit since they no longer have to find where VSIXInstaller.exe is installed. The command line passed to VSIXBootstrapper.exe is passed through to VSIXInstaller.exe.”

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.

VS SDK, packages, add-ins, macros and more…