dotnet/msbuild

Canceling a build can fail if a process running on the computer is protected

Open

#6.477 aberto em 24 de mai. de 2021

Ver no GitHub
 (3 comments) (2 reactions) (0 assignees)C# (1.364 forks)batch import
bughelp wantedtriaged

Métricas do repositório

Stars
 (5.062 stars)
Métricas de merge de PR
 (Mesclagem média 11d 7h) (125 fundiu PRs em 30d)

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 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

Attach a binlog

Guia do colaborador