Canceling a build can fail if a process running on the computer is protected
- Dominant language
- C#
- Stars
- 5.5k
- Forks
- 1.5k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 133
Description
### Issue Description
Canceling a NMake project (not relevant here but it's Unreal Engine 4) through CTRL+BREAK in Visual Studio doesn't work when some (unrelated) processes are running on the computer.
### Steps to Reproduce
Prerequisites: have a process running which will deny access to its start time (for instance, OpenVPN, see Analysis section).
1. Build Project in Visual Studio
2. Cancel build (CTRL+BREAK)
Notice an MSBuild error (see Actual Behavior section).
### Expected Behavior
We should be able to cancel a build without an error. (Or at least just #5508 😄)
### Actual Behavior
Cancelling a build doesn't work and provokes the following error:
```
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): warning MSB5021: Terminating the task executable "cmd" and its child processes because the build was canceled.
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: The specified task executable "cmd.exe" could not be run. System.ComponentModel.Win32Exception (0x80004005): Access is denied
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at System.Diagnostics.ProcessManager.OpenProcess(Int32 processId, Int32 access, Boolean throwIfExited)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at System.Diagnostics.Process.GetProcessHandle(Int32 access, Boolean throwIfExited)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at System.Diagnostics.Process.GetProcessTimes()
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at System.Diagnostics.Process.get_StartTime()
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Shared.NativeMethodsShared.GetChildProcessIds(Int32 parentProcessId, DateTime parentStartTime)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Shared.NativeMethodsShared.KillTree(Int32 processIdToKill)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Utilities.ProcessExtensions.KillTree(Process process, Int32 timeout)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Utilities.ToolTask.KillToolProcessOnTimeout(Process proc, Boolean isBeingCancelled)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Utilities.ToolTask.TerminateToolProcess(Process proc, Boolean isBeingCancelled)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Utilities.ToolTask.HandleToolNotifications(Process proc)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Utilities.ToolTask.ExecuteTool(String pathToTool, String responseFileCommands, String commandLineCommands)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Tasks.Exec.ExecuteTool(String pathToTool, String responseFileCommands, String commandLineCommands)
2>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(46,5): error MSB6003: at Microsoft.Build.Utilities.ToolTask.Execute()
```
### Analysis
Looking at the code, I think the issue is with [Shared.NativeMethodsShared.GetChildProcessIds](https://github.com/dotnet/msbuild/blob/97ba42a391b459caca040301165eeb5d4464bd66/src/Shared/NativeMethodsShared.cs#L1355 ) which fails if, while iterating through the processes currently running, one of them is protected.
In my case, I managed to identify the faulty process: [OpenVPN 2.5.1](https://openvpn.net/community-downloads/). When connected to the VPN, it creates a process which makes `GetChildProcessIds()` fail.
More precisely, [Process.StartTime](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.starttime?view=net-5.0) throws Win32Exception 0x80004005 "Access is denied" and it's not caught.
I also managed to reproduce the error by copying the following code from msbuild in a simple project and calling it:
```
[SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
[DllImport("KERNEL32.DLL")]
private static extern SafeProcessHandle OpenProcess(eDesiredAccess dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
internal static List> GetChildProcessIds(int parentProcessId, DateTime parentStartTime)
{
List> myChildren = new List>();
foreach (Process possibleChildProcess in Process.GetProcesses())
{
using (possibleChildProcess)
{
Log.TraceInformation("Process:");
Log.TraceInformation(possibleChildProcess.ToString());
// Hold the child process handle open so that children cannot die and restart with a different parent after we've started looking at it.
// This way, any handle we pass back is guaranteed to be one of our actual children.
SafeProcessHandle childHandle = OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION, false, possibleChildProcess.Id);
if (childHandle.IsInvalid)
{
continue;
}
bool keepHandle = false;
try
{
if (possibleChildProcess.StartTime > parentStartTime)
{
}
}
finally
{
if (!keepHandle)
{
childHandle.Dispose();
}
}
}
}
return myChildren;
}
```
### Versions & Configurations
16.9.0.16703
### Attach a binlog
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.