简体   繁体   English

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

[英]How terminate child processes when parent process terminated in C#

Task: Auto kill all child processes if parent process terminate. 任务:如果父进程终止,则自动终止所有子进程。 Parent procees can be terminated not only in correct way, but also by killing in ProcessExplorer, for example. 父进程不仅可以以正确的方式终止,还可以通过在ProcessExplorer中进行终止来终止。 How can I do it? 我该怎么做?

Similar question in С topic advice to use Job objects. С主题建议中使用Job对象的类似问题。 How to use it in C# without exporting external DLL? 如何在C#中使用它而不导出外部DLL?


I tried to use Job Objects. 我试着使用Job Objects。 But this code doesn't work properly: 但是这段代码不能正常工作:

  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 returns with error. PInvoke.SetInformationJobObject返回错误。 GetLastError returns error 24. However, PInvoke.AssignProcessToJobObject works and child process added to Job Queue (I can see it in ProcessExplorer). GetLastError返回错误24.但是,PInvoke.AssignProcessToJobObject工作,子进程添加到作业队列(我可以在ProcessExplorer中看到它)。 But, because PInvoke.SetInformationJobObject don't work - spawned process stay alive when I kill parent one. 但是,因为PInvoke.SetInformationJobObject不起作用 - 当我杀死父节点时,生成的进程保持活动状态。

What do I have incorrect in this code? 我在这段代码中有什么不正确的地方?

I tried the code above and indeed, it does not work, complaining of a bad size. 我尝试了上面的代码,实际上,它不起作用,抱怨一个不好的大小。 The reason for this is that the structure used changes size depending on the host platform; 原因是所使用的结构根据主机平台改变大小; the original code fragment (seen on a dozen websites) assumes a 32 bit application. 原始代码片段(在十几个网站上看到)假设一个32位应用程序。

Switch the structure to this (note the IntPtr resizing members) and it will work. 将结构切换到此(请注意IntPtr调整大小成员),它将起作用。 At least it did for me. 至少它对我有用。

[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;
}

To kill a process tree on windows, given only the parent process or process id, you'll need to walk the process tree. 要在Windows上终止进程树,只给出父进程或进程ID,您需要遍历进程树。

For that, you'll need a way to get the parent process id for a given process. 为此,您需要一种方法来获取给定进程的父进程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;
    }
}

Once you have that, actually killing the tree is not hard. 一旦你拥有了它,实际上杀死树并不难。

class Program
{
    /// <summary>
    /// Kill specified process and all child processes
    /// </summary>
    static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.WriteLine("Usage: KillProcessTree <pid>");
            return;
        }

        int pid = int.Parse(args[0]);

        Process root = Process.GetProcessById(pid);
        if (root != null)
        {
            Console.WriteLine("KillProcessTree " + pid);

            var list = new List<Process>();
            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);
        }
    }

    /// <summary>
    /// Get process and children
    /// We use postorder (bottom up) traversal; good as any when you kill a process tree </summary>
    /// </summary>
    /// <param name="plist">Array of all processes</param>
    /// <param name="parent">Parent process</param>
    /// <param name="output">Output list</param>
    /// <param name="indent">Indent level</param>
    private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> 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

You can pass ProcessID of the parent process as an argument to the child process. 您可以将父进程的ProcessID作为参数传递给子进程。 And then child processes will be responsible for checking from time to time whether the parent process still running. 然后子进程将负责不时检查父进程是否仍在运行。 (By calling Process.GetProcessById .) (通过调用Process.GetProcessById 。)

Another way to track existence of the parent process is to use Mutex synchronization primitive. 跟踪父进程存在的另一种方法是使用Mutex同步原语。 Parent application will initially create a global mutex with the name known by children. 父应用程序最初将创建一个名为children的全局互斥锁。 Children can check from time to time whether the mutex still exists and terminate if not. 孩子们可以不时检查互斥锁是否仍然存在,如果没有则终止。 (Once the parent process is closed the mutex will be destroyed by the system automatically, regardless of the way it way closed.) (一旦父进程关闭,系统将自动销毁互斥锁​​,无论其关闭的方式如何。)

Did you pay attention to the error code? 你注意错误代码了吗? Error 24 is ERROR_BAD_LENGTH , which probably means that 48 isn't the right length of the structure. 错误24是ERROR_BAD_LENGTH ,这可能意味着48不是结构的正确长度。 I think it's 44, but you should do a sizeof to be sure. 我认为这是44,但你应该做一个确定的sizeof

Windows does not force child processes to close when a parent process closes. 父进程关闭时,Windows不会强制关闭子进程。 When you select "Kill Tree" in a tool like Task Manager or Process explorer, the tool actually finds all child processes and kill them one by one. 当您在任务管理器或进程资源管理器等工具中选择“杀死树”时,该工具实际上会找到所有子进程并逐个终止它们。

If you want to ensure that child processes are cleaned when your application terminates, you can create a ProcessManager class that implements IDisposable that actually creates the processes, keeps track of their instances and calls Kill on each one of them on Dispose, eg 如果要确保在应用程序终止时清除子进程,可以创建一个实现IDisposable的ProcessManager类,实际创建进程,跟踪它们的实例并在Dispose上调用它们中的每一个,例如:

public class ProcessManager:IDisposable
{
    List<Process> processes=new List<Process>();

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM