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).
- Build Project in Visual Studio
- 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 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. When connected to the VPN, it creates a process which makes GetChildProcessIds()
fail.
More precisely, Process.StartTime 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<KeyValuePair<int, SafeProcessHandle>> GetChildProcessIds(int parentProcessId, DateTime parentStartTime)
{
List<KeyValuePair<int, SafeProcessHandle>> myChildren = new List<KeyValuePair<int, SafeProcessHandle>>();
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