private static Dictionary <uint, ReportedProcess> GetSurvivingChildProcesses(JobObject jobObject) { if (!jobObject.TryGetProcessIds(out uint[] survivingChildProcessIds) || survivingChildProcessIds.Length == 0) { return(null); } var survivingChildProcesses = new Dictionary <uint, ReportedProcess>(); foreach (uint processId in survivingChildProcessIds) { using (SafeProcessHandle processHandle = ProcessUtilities.OpenProcess( ProcessSecurityAndAccessRights.PROCESS_QUERY_INFORMATION | ProcessSecurityAndAccessRights.PROCESS_VM_READ, false, processId)) { if (processHandle.IsInvalid) { // we are too late: could not open process continue; } if (!jobObject.ContainsProcess(processHandle)) { // we are too late: process id got reused by another process continue; } int exitCode; if (!ProcessUtilities.GetExitCodeProcess(processHandle, out exitCode)) { // we are too late: process id got reused by another process continue; } using (PooledObjectWrapper <StringBuilder> wrap = Pools.GetStringBuilder()) { StringBuilder sb = wrap.Instance; if (sb.Capacity < MaxProcessPathLength) { sb.Capacity = MaxProcessPathLength; } if (ProcessUtilities.GetModuleFileNameEx(processHandle, IntPtr.Zero, sb, (uint)sb.Capacity) <= 0) { // we are probably too late continue; } // Attempt to read the process arguments (command line) from the process // memory. This is not fatal if it does not succeed. string processArgs = string.Empty; var basicInfoSize = (uint)Marshal.SizeOf <Native.Processes.Windows.ProcessUtilitiesWin.PROCESS_BASIC_INFORMATION>(); var basicInfoPtr = Marshal.AllocHGlobal((int)basicInfoSize); uint basicInfoReadLen; try { if (Native.Processes.Windows.ProcessUtilitiesWin.NtQueryInformationProcess( processHandle, Native.Processes.Windows.ProcessUtilitiesWin.ProcessInformationClass.ProcessBasicInformation, basicInfoPtr, basicInfoSize, out basicInfoReadLen) == 0) { Native.Processes.Windows.ProcessUtilitiesWin.PROCESS_BASIC_INFORMATION basicInformation = Marshal.PtrToStructure <Native.Processes.Windows.ProcessUtilitiesWin.PROCESS_BASIC_INFORMATION>(basicInfoPtr); Contract.Assert(basicInformation.UniqueProcessId == processId); // NativeMethods.ReadProcessStructure and NativeMethods.ReadUnicodeString handle null\zero addresses // passed into them. Since these are all value types, then there is no need to do any type // of checking as passing zero through will just result in an empty process args string. var peb = Native.Processes.Windows.ProcessUtilitiesWin.ReadProcessStructure <Native.Processes.Windows.ProcessUtilitiesWin.PEB>(processHandle, basicInformation.PebBaseAddress); var processParameters = Native.Processes.Windows.ProcessUtilitiesWin.ReadProcessStructure <Native.Processes.Windows.ProcessUtilitiesWin.RTL_USER_PROCESS_PARAMETERS>(processHandle, peb.ProcessParameters); processArgs = Native.Processes.Windows.ProcessUtilitiesWin.ReadProcessUnicodeString(processHandle, processParameters.CommandLine); } } finally { Marshal.FreeHGlobal(basicInfoPtr); } string path = sb.ToString(); survivingChildProcesses.Add(processId, new ReportedProcess(processId, path, processArgs)); } } } return(survivingChildProcesses); }