父进程在C#中终止时如何终止子进程

任务:如果父进程终止,则自动终止所有子进程。 父进程不仅可以以正确的方式终止,还可以通过在ProcessExplorer中进行终止来终止。 我该怎么做?

在С主题建议中使用Job对象的类似问题。 如何在C#中使用它而不导出外部DLL?


我试着使用Job Objects。 但是这段代码不能正常工作:

var job = PInvoke.CreateJobObject(null, null); var jobli = new PInvoke.JOBOBJECT_BASIC_LIMIT_INFORMATION(); jobli.LimitFlags = PInvoke.LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_PRIORITY_CLASS | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_JOB_TIME | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_JOB_MEMORY; var res = PInvoke.SetInformationJobObject(job, PInvoke.JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation, jobli, 48); if (!res) { int b = PInvoke.GetLastError(); Console.WriteLine("Error " + b); } var Prc = Process.Start(...); PInvoke.AssignProcessToJobObject(job, Prc.Handle); 

PInvoke.SetInformationJobObject返回错误。 GetLastError返回错误24.但是,PInvoke.AssignProcessToJobObject工作,子进程添加到作业队列(我可以在ProcessExplorer中看到它)。 但是,因为PInvoke.SetInformationJobObject不起作用 – 当我杀死父节点时,生成的进程保持活动状态。

我在这段代码中有什么不正确的地方?

我尝试了上面的代码,事实上,它不起作用,抱怨大小不好。 这样做的原因是所使用的结构根据主机平台改变了大小; 原始代码片段(在十几个网站上看到)假设一个32位应用程序。

将结构切换到此(请注意IntPtrresize成员),它将起作用。 至少它对我有用。

 [StructLayout(LayoutKind.Sequential)] struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public Int16 LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public Int16 ActiveProcessLimit; public Int64 Affinity; public Int16 PriorityClass; public Int16 SchedulingClass; } 

要在Windows上终止进程树,只给出父进程或进程ID,您需要遍历进程树。

为此,您需要一种方法来获取给定进程的父进程ID。

 using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Diagnostics; using System.Management; namespace KillProcessTree { public static class MyExtensions { public static int GetParentProcessId(this Process p) { int parentId = 0; try { ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'"); mo.Get(); parentId = Convert.ToInt32(mo["ParentProcessId"]); } catch (Exception ex) { Console.WriteLine(ex.ToString()); parentId = 0; } return parentId; } } 

一旦你拥有了它,实际上杀死树并不难。

 class Program { ///  /// Kill specified process and all child processes ///  static void Main(string[] args) { if (args.Length < 1) { Console.WriteLine("Usage: KillProcessTree "); return; } int pid = int.Parse(args[0]); Process root = Process.GetProcessById(pid); if (root != null) { Console.WriteLine("KillProcessTree " + pid); var list = new List(); GetProcessAndChildren(Process.GetProcesses(), root, list, 1); // kill each process foreach (Process p in list) { try { p.Kill(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } } else { Console.WriteLine("Unknown process id: " + root); } } ///  /// Get process and children /// We use postorder (bottom up) traversal; good as any when you kill a process tree  ///  /// Array of all processes /// Parent process /// Output list /// Indent level private static void GetProcessAndChildren(Process[] plist, Process parent, List output, int indent) { foreach (Process p in plist) { if (p.GetParentProcessId() == parent.Id) { GetProcessAndChildren(plist, p, output, indent + 1); } } output.Add(parent); Console.WriteLine(String.Format("{0," + indent*4 + "} {1}", parent.Id, parent.MainModule.ModuleName)); } } } // namespace 

您可以将父进程的ProcessID作为参数传递给子进程。 然后子进程将负责不时检查父进程是否仍在运行。 (通过调用Process.GetProcessById 。)

跟踪父进程存在的另一种方法是使用Mutex同步原语。 父应用程序最初将创建一个名为children的全局互斥锁。 孩子们可以不时检查互斥锁是否仍然存在,如果没有则终止。 (一旦父进程关闭,系统将自动销毁互斥锁​​,无论它是如何关闭的。)

你注意错误代码了吗? 错误24是ERROR_BAD_LENGTH ,这可能意味着48不是结构的正确长度。 我认为这是44,但你应该做一个确定的sizeof

当父进程关闭时,Windows不会强制关闭子进程。 当您在任务管理器或进程资源管理器等工具中选择“杀戮树”时,该工具实际上会找到所有子进程并逐个终止它们。

如果要确保在应用程序终止时清除子进程,可以创建一个实现IDisposable的ProcessManager类,它实际创建进程,跟踪它们的实例并在Dispose上调用它们中的每一个,例如:

 public class ProcessManager:IDisposable { List processes=new List(); public Process Start(ProcessStartInfo info) { var newProcess = Process.Start(info); newProcess.EnableRaisingEvents = true processes.Add(newProcess); newProcess.Exited += (sender, e) => processes.Remove(newProcess); return newProcess; } ~ProcessManager() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { foreach (var process in processes) { try { if (!process.HasExited) process.Kill(); } catch{} } } }