17

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

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


我尝试使用作业对象。但是这段代码不能正常工作:

  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 不起作用 - 当我杀死父进程时,生成的进程仍然存在。

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

4

5 回答 5

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;
}
于 2011-05-12T09:46:37.693 回答
9

要杀死 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
于 2011-04-15T17:08:02.503 回答
4

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

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

于 2010-07-13T09:00:59.870 回答
3

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

于 2010-07-22T12:18:42.413 回答
2

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

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

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{}                    
        }
    }
}
于 2010-07-13T09:35:25.417 回答