Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSBuildWorkspace.OpenProjectAsync fails when dotnet is not pre-installed in the system #77640

Open
framar-RA opened this issue Mar 17, 2025 · 4 comments
Assignees
Labels

Comments

@framar-RA
Copy link

framar-RA commented Mar 17, 2025

Version Used:

.NET: 8.0
sdk: 8.0.406
Microsoft.CodeAnalysis.CSharp.Workspaces: 4.12.0
Microsoft.CodeAnalysis.Workspaces.MSBuild: 4.12.0
Microsoft.Build: 17.8.3
Microsoft.Build.Locator: 1.6.10

Description:

We encountered a problem that prevents us from upgrading the NuGet dependencies of our project (specifically Microsoft.CodeAnalysis.CSharp.Workspaces and Microsoft.CodeAnalysis.Workspaces.MSBuild) from version 4.7 to 4.12.

We have a C++ project that we distribute along with .NET. The main app communicates with a C# handler, which tries to open and analyze an external C# project by executing the following lines of code:

MSBuildLocator.RegisterMSBuildPath(dotnetSdkDirectory);
using var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(Path.Combine(assemblyDirectory, $"{assemblyName}.csproj")).Result;

Upgrading the aforementioned NuGet packages from version 4.7 to version 4.12 causes the function call OpenProjectAsync to fail if our application is installed on systems where .NET is not preinstalled.

Steps to Reproduce:

Despite being unable to reproduce this problem with a minimal working example, we managed to reproduce this issue in a VM with Windows 10. We analyzed the differences between the two versions of the NuGet packages by connecting a remote debugger to our running application. Specifically, these additional lines of code were not present in version 4.7 but can be found in version 4.12 of Microsoft.CodeAnalysis.Workspaces.MSBuild.

 var preferredBuildHostKind = BuildHostProcessManager.GetKindForProject(projectPath);
var (buildHost, actualBuildHostKind) = await _buildHostProcessManager.GetBuildHostWithFallbackAsync(preferredBuildHostKind, projectPath, cancellationToken

Starting the BuildHostProcess (.NET Core) causes the error: the dotnet executable seems to be launched from the working directory "", (from the debugger we see processStartInfo.workingDirectory = ""), which, in general, may not contain dotnet.exe. As a consequence, the PATH environment variable is used. It would be nice to have a way to specify where to look for dotnet.exe to prevent this issue from occurring when .NET is not installed on the system.

Expected Behavior:

No exception is thrown. The function call succeeds.

Actual Behavior:

An exception is thrown:

---> System.ComponentModel.Win32Exception (2): An error occurred trying to start process 'dotnet.exe' with working directory 'C:\Users\USER\Desktop\tmp'. The system cannot find the file specified.
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
   at Microsoft.CodeAnalysis.MSBuild.BuildHostProcessManager.GetBuildHostAsync(BuildHostProcessKind buildHostKind, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.BuildHostProcessManager.GetBuildHostWithFallbackAsync(BuildHostProcessKind buildHostKind, String projectOrSolutionFilePath, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.Worker.LoadProjectFileInfosAsync(String projectPath, DiagnosticReportingOptions reportingOptions, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.Worker.LoadProjectInfosFromPathAsync(String projectPath, DiagnosticReportingOptions reportingOptions, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.Worker.LoadAsync(CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadProjectInfoAsync(String projectFilePath, ProjectMap projectMap, IProgress`1 progress, ILogger msbuildLogger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.MSBuildProjectLoader.LoadProjectInfoAsync(String projectFilePath, ProjectMap projectMap, IProgress`1 progress, ILogger msbuildLogger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.OpenProjectAsync(String projectFilePath, ILogger msbuildLogger, IProgress`1 progress, CancellationToken cancellationToken)

Additional Note:

  • Further analysis revealed that the problem arises from version 4.8 to 4.9.
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Mar 17, 2025
@framar-RA framar-RA changed the title MSBuildWorkspace.OpenProjectAsync fails when dotnet is distributed with the main application and not pre-installed in the MSBuildWorkspace.OpenProjectAsync fails when dotnet is not pre-installed in the system Mar 17, 2025
@deepakrathore33 deepakrathore33 removed the untriaged Issues and PRs which have not yet been triaged by a lead label Mar 17, 2025
@jasonmalinowski
Copy link
Member

@framar-RA: just trying to understand your scenario a bit more here, is the expectation that:

  1. If you're analyzing .NET Framework projects we shouldn't accidentally try launching .NET processes? Because I'd generally expect it to be launching a .NET Core build host only if we think one of your projects are SDK projects that need it.
  2. You're launching us within the context of some private app host (i.e. you're a self-contained app, or you have a private dotnet which isn't on the PATH).

If it's the former we should see why it's thinking you need to analyze an SDK-style project; if it's the latter then that's definitely not good.

It would be nice to have a way to specify where to look for dotnet.exe to prevent this issue from occurring when .NET is not installed on the system.

If you're already running under dotnet.exe I'd expect that to work since there's some obscure logic in the .NET runtime which will use the dotnet.exe you're in if your process is dotnet.exe itself. Otherwise this could be a potential dupe of #71019 if that's ultimately what we're not doing right.

@framar-RA
Copy link
Author

framar-RA commented Mar 18, 2025

The latter scenario describe pretty well the context in which we operate. In particular we have a private dotnet which is not on the PATH.

If you're already running under dotnet.exe I'd expect that to work since there's some obscure logic in the .NET runtime which will use the dotnet.exe you're in if your process is dotnet.exe itself

I am working to provide you with more precise information about this. For the moment I can tell you that C# handler is build as a dll and the methods are exposed with COM (to the main c++ app).

@jasonmalinowski
Copy link
Member

@framar-RA Is it still a dotnet.exe process or something running as a custom app host?

@framar-RA
Copy link
Author

@jasonmalinowski Sorry for the late reply. As a personal note, I must admit I have limited knowledge about the COM interop process and the part of our codebase responsible for these operations. Sadly, filling this gap is taking more time than expected.

That said, I cannot confidently answer your question, but I can give you some insight into the minimal working example we failed to set up to reproduce the issue, which may shed some light on the situation.

In particular, we tried to define a C# handler using Roslyn on a VM without .NET installed. This setup mimics the one of our app WITHOUT the need to rely on interop (i.e., the main app is a C# handler, thus compiled and run with dotnet.exe).

- Example
    - Handler
         - handler.csproj
         - ...
    - CSProjToBeHandled  
         - CSProjToBeHandled.csproj
         - ...
    - CustomSDK
         - dotnet.exe
         - ...
         - sdk (dir)
             - ... (sdk related files) ... 

As expected, we were not able to reproduce the error this way since we ran the handler using the dotnet.exe distributed within the example. As you suggested, the .NET runtime is capable of "detecting" the dotnet.exe of the process being launched.
(If you feel this might be of any help, I can set up a repo. Still, you will not be able to reproduce the error this way.)

Moreover, a quick check revealed that during the execution of the C# handler in our main app (not the example), only one dotnet process is active, which is the following:

"[OurAppInstallationFolder]\Tools\DotNetSdk\dotnet.exe" exec "[OurAppInstallationFolder]\Tools\DotNetSdk\sdk\8.0.406\Roslyn\bincore\VBCSCompiler.dll" "-pipename:CVEuC54EdbNx6ibQ_ni6uI+K3gPIOn1KLRKXfjOW8rU"	Disabled

If we were running as a dotnet.exe process, I was expecting to see a similar process launching one of our DLLs, but this is not present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants