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

时间:2010-07-13 08:07:21

标签: c# process jobs terminate termination

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

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


我尝试使用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不起作用 - 当我杀死父节点时,生成的进程保持活着。

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

5 个答案:

答案 0 :(得分:9)

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

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

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

答案 1 :(得分:7)

要在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
{
    /// <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

答案 2 :(得分:4)

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

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

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

如果要确保在应用程序终止时清除子进程,可以创建一个实现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{}                    
        }
    }
}