msbuild: msbuild nuget package unable to open vs2017 csproj files

I’m referencing the nuget packages Microsoft.Build and Microsoft.Build.Tasks.Core. It doesn’t matter if I chose stable or prerelease, both versions run into the same error.

I’m trying to open a csproj file for evaluation:

var projectFile = @"C:\projects\test\test.csproj";
var project = new Microsoft.Build.Evaluation.Project(projectFile);
var projectName = Path.GetFileNameWithoutExtension(projectFile);
var outputFile = project.GetPropertyValue("TargetPath");
var outputName = Path.GetFileName(outputFile);

I’m getting Microsoft.Build.Exceptions.InvalidProjectFileException: 'The tools version "15.0" is unrecognized. Available tools versions are "12.0", "14.0", "2.0", "3.5", "4.0".'

When searching the web for this error message I’m only coming up with things related to the VS 2017 installation and how to look up the msbuild packaged with it; unfortunately this doesn’t help me with my instance of the exception because I’m invoking msbuild as a library and not as a process.

Do I have to tell the nuget assemblies where the VS 2017 installation is? How do I do this? (I was assuming the nuget assemblies can work stand alone, but if a VS installation is required that works too, it’s just not discoverable for me what to do here.)

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 14
  • Comments: 72 (23 by maintainers)

Commits related to this issue

Most upvoted comments

@weltkante Yes, if you have that information already, there’s an easy workaround.

Set the environment variables VSINSTALLDIR and VisualStudioVersion before calling into MSBuild APIs.

I just confirmed on my machine with this hardcoded value on top of your example code:

Environment.SetEnvironmentVariable("VSINSTALLDIR", @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community");
Environment.SetEnvironmentVariable("VisualStudioVersion", @"15.0");

The problem is arising because MSBuild attempts to build from the installed version of Visual Studio where possible, but we’re failing to locate it now (still not sure why; continuing to look). Setting those environment variables lets an alternate codepath through the find-toolset code take over.

You can and should use the VS copy of MSBuild, but since it’s no longer in the GAC you must locate it. https://docs.microsoft.com/en-us/visualstudio/msbuild/updating-an-existing-application has details on what’s required, and https://github.com/Microsoft/MSBuildLocator/ is a package used to make the process easier.

Ok, here’s the source of the problem:

https://github.com/Microsoft/msbuild/blob/ab090d1255caa87e742cbdbc6d7fe904ecebd975/src/Shared/BuildEnvironmentHelper.cs#L232-L235

The returned instance of VS (on this machine) has version 15.3.26730.3, which doesn’t match CurrentVisualStudioVersion which is 15.0.

Need to track down an Update 2 machine to see if this is a recent change in the return value that we need to ping the setup folks about (I don’t see how it could have been working otherwise).

Also need to make our code more robust. Not quite sure how yet.

@rainersigwald That’s an awful lot of dicking around for something that used to be a checkbox.

Ok resolved the above by adding Microsoft.Build.Tasks.Core nuget package to my project. I then got an error when trying to run the Pack target:

Task "PackTask"

    C:\Program Files\dotnet\sdk\2.0.0-preview2-006497\Sdks\NuGet.Build.Tasks.Pack\buildCrossTargeting\NuGet.Build.Tasks.Pack.targets(141,5): error MSB4127: The "PackTask" task could not be instantiated from the assembly "C:\Program Files\dotnet\sdk\2.0.0-preview2-006497\Sdks\NuGet.Build.Tasks.Pack\buildCrossTargeting\..\Desktop\NuGet.Build.Tasks.Pack.dll". Please verify the task assembly has been built using the same version of the Microsoft.Build.Framework assembly as the one installed on your computer and that your host application is not missing a binding redirect for Microsoft.Build.Framework. Unable to cast object of type 'NuGet.Build.Tasks.Pack.PackTask' to type 'Microsoft.Build.Framework.ITask'.

    C:\Program Files\dotnet\sdk\2.0.0-preview2-006497\Sdks\NuGet.Build.Tasks.Pack\buildCrossTargeting\NuGet.Build.Tasks.Pack.targets(141,5): error MSB4060: The "PackTask" task has been declared or used incorrectly, or failed during construction. Check the spelling of the task name and the assembly name.

The error message prompted me to check binding redirects in app.config - was missing a binding redirect for some reason. Adding the folliwng seems to have fixed it:

<dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
      </dependentAssembly>

@Kryptos-FR Yes, that’s exactly right. We’re working on #2030 to provide a NuGet package that makes that easier to do (and includes loading the MSBuild assemblies from the same installed VS).

@GeirGrusom I think you might like to use MSBuildLocator in your application. It handles the scenarios you’re currently using VSWhere for, and you should be able to use a single application to load both 2017 and 2019 projects (as long as you reference MSBuild 15.x when you build).

@raffaeler Thanks! Unfortunately, I do not see the same thing on my machine, also with 15.5.27130.2010.

My attempt to repro was:

  • Create new .NET 4.6.1 project from template.
  • Add reference to Microsoft.CodeAnalysis 2.6.1.
  • Do an OpenSolutionAsync and some minimal work.

This failed with a similar-but different error:

Msbuild failed when processing the file 'C:\src\RoslynApiConsumer\RoslynApiConsumer\RoslynApiConsumer.csproj' with message: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn\Microsoft.CSharp.Core.targets: (84, 5): The "Microsoft.CodeAnalysis.BuildTasks.Csc" task could not be loaded from the assembly C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn\Microsoft.Build.Tasks.CodeAnalysis.dll. Could not load file or assembly 'Microsoft.Build.Utilities.Core, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified. Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.

That’s because I forgot to add binding redirects (as mentioned above in https://github.com/Microsoft/msbuild/issues/2369#issuecomment-327033965). I added this to my app.config:

<dependentAssembly>
  <assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name="Microsoft.Build" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name="Microsoft.Build.Utilities.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name="Microsoft.Build.Tasks.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>

And loading the projects completes without errors.

Follow-up questions:

  • Are you specifying binding redirects?
  • What kinds of project(s) are you attempting to load?
  • What happens if you build RoslynApiConsumer.zip and run it – does it still error? If so can you give its output?

Thx @artiomchi !

I can confirm you that with version 15.5.0-preview-000072-0942130 I do not need the hack anymore.

Any chance to get this in a 15.3 patch?

In my case I need my library to work on computers which do not have VS2017 installed so I tweaked a little bit the workaround. I’m sharing it here in case it helps others:

private static void HackForVs2017Update3()
{
    var registryKey = $@"SOFTWARE{(Environment.Is64BitProcess ? @"\Wow6432Node" : string.Empty)}\Microsoft\VisualStudio\SxS\VS7";
    using (RegistryKey subKey = Registry.LocalMachine.OpenSubKey(registryKey))
    {
        string visualStudioPath = subKey?.GetValue("15.0") as string;
        if (!string.IsNullOrEmpty(visualStudioPath))
        {
            Environment.SetEnvironmentVariable("VSINSTALLDIR", visualStudioPath);
            Environment.SetEnvironmentVariable("VisualStudioVersion", @"15.0");
        }
    }
}

Obviously I won’t be able to load 15.0 projects in this case.

I was able to make it work by setting the VSINSTALLDIR and VisualStudioVersion. Although there was a caveat that I was not expecting (but which still makes sense somehow): you need to set those variables before calling any of the Microsoft.Build API (especially ProjectCollection) otherwise the list of toolsets is cached and you will not be able to find the one for “15.0” after that.

This means that in order to do it properly one has to follow these steps in order:

  1. List all instances of Visual Studio installed using Microsoft.VisualStudio.Setup.Configuration
    • The path can be retrieved from ISetupInstance2.GetInstallationPath()
    • Note that this also lists Build Tools for Visual Studio which is perfect in my case
  2. Set the environment variables
Environment.SetEnvironmentVariable("VSINSTALLDIR", installationPath);
Environment.SetEnvironmentVariable("VisualStudioVersion", @"15.0");
  1. Start using Microsoft.Build API, for example:
var projectCollection = new Microsoft.Build.Evaluation.ProjectCollection();
if (projectCollection.GetToolset("15.0") == null)
{
    throw new Exception("MSBuild 15 not found");
}

In case you need the full path to MSBuild.exe it should be in Path.Combine(installationPath, "MSBuild", "15.0", "Bin").

Note: it is still going under some testing but it is my hope that this will work for the time being, until the fix is released in the next MSBuild.