Убить дерево процессов программно в C#

Я запускаю Internet Explorer программно с кодом, который выглядит следующим образом:

ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);

это создает 2 процесса, видимых в Диспетчере задач Windows. Затем я пытаюсь убить процесс с помощью:

ieProcess.Kill();

это приводит к тому, что один из процессов в Диспетчере задач закрывается, а другой остается. Я попытался проверить любые свойства, которые будут иметь дочерние процессы, но не нашел ни одного. Как я могу убить и другой процесс? В целом, как вы убиваете все процессы, связанные с процессом, который вы начинаете процесс.Начать?

9 ответов


это сработало очень хорошо для меня:

/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
    // Cannot close 'system idle process'.
    if (pid == 0)
    {
        return;
    }
    ManagementObjectSearcher searcher = new ManagementObjectSearcher
            ("Select * From Win32_Process Where ParentProcessID=" + pid);
    ManagementObjectCollection moc = searcher.Get();
    foreach (ManagementObject mo in moc)
    {
        KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
    }
    try
    {
        Process proc = Process.GetProcessById(pid);
        proc.Kill();
    }
    catch (ArgumentException)
    {
        // Process already exited.
    }
}

обновление 2016-04-26

проверено на Visual Studio 2015 Update 2 on Win7 x64. По-прежнему работает так же хорошо, как и 3 года назад.

обновление 2017-11-14

добавлена проверка процесса простоя системы if (pid == 0)

обновление 2018-03-02

необходимо добавить ссылку на System.Management пространство имен, см. комментарий от @MinimalTech ниже. Если у Вас установлен ReSharper, он предложит сделать это за вас автоматически.


вы должны позвонить Process.CloseMainWindow() отправить сообщение в Главное окно процесса. Подумайте об этом, когда пользователь нажимает кнопку " X " закрыть или | выход пункт меню.

безопаснее отправить сообщение в Internet Explorer, чтобы закрыть себя, чем идти и убивать все свои процессы. Эти процессы могут делать что угодно, и вам нужно позволить IE делать свое дело и закончить, прежде чем просто убить его в середине делать что-то, что может быть важно для будущих запусков. Это справедливо для любой программы, которую вы убиваете.


Я не поклонник любого из представленных здесь решений.

вот что я придумал:

private static void EndProcessTree(string imageName)
{
    Process.Start(new ProcessStartInfo
    {
        FileName = "taskkill",
        Arguments = $"/im {imageName} /f /t",
        CreateNoWindow = true,
        UseShellExecute = false
    }).WaitForExit();
}

как использовать:

EndProcessTree("chrome.exe");

Если кто-то заинтересован, я взял один из ответов с другой страницы и немного изменил его. Теперь это автономный класс со статическими методами. Это не имеют правильную обработку ошибок или ведение журнала. Модифицируйте, чтобы использовать для своих нужд. Предоставление корневого процесса KillProcessTree сделает это.

class ProcessUtilities
{
    public static void KillProcessTree(Process root)
    {
        if (root != null)
        {
            var list = new List<Process>();
            GetProcessAndChildren(Process.GetProcesses(), root, list, 1);

            foreach (Process p in list)
            {
                try
                {
                    p.Kill();
                }
                catch (Exception ex)
                {
                    //Log error?
                }
            }
        }
    }

    private static int GetParentProcessId(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;
    }

    private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> output, int indent)
    {
        foreach (Process p in plist)
        {
            if (GetParentProcessId(p) == parent.Id)
            {
                GetProcessAndChildren(plist, p, output, indent + 1);
            }
        }
        output.Add(parent);
    }
}

другое решение-использовать команду taskill. Я использую следующий код в своих приложениях:

public static void Kill()
{
    try
    {
            ProcessStartInfo processStartInfo = new ProcessStartInfo("taskkill", "/F /T /IM your_parent_process_to_kill.exe")
            {
                WindowStyle = ProcessWindowStyle.Hidden,
                CreateNoWindow = true,
                UseShellExecute = false,
                WorkingDirectory = System.AppDomain.CurrentDomain.BaseDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            };
            Process.Start(processStartInfo);
    }
    catch { }
}

Если кому-то нужно решение dotnet core, dotnet cli придумал реализацию, основанную на taskill как упоминалось выше и рекурсивно pgrep / kill для систем на базе unix. полная реализация может быть найдена на github. К сожалению, класс является внутренним, поэтому вам придется скопировать его в свою базу кода.

список дочерних процессов (должен выполняться рекурсивно):

$"pgrep -P {parentId}"

убить процесс:

$"kill -TERM {processId}"

вы используете IE8 или IE9? Это абсолютно запустило бы более одного процесса из-за его новой многопроцессной архитектуры. Во всяком случае, взгляните на этот и другие ответы для получения дерева процессов и его убийства.


другой подход, который может быть очень полезен с помощью Windows API для объектов заданий. Процесс может быть назначен объекту задания. Дочерние процессы такого процесса автоматически назначаются одному и тому же объекту задания.

все процессы, назначенные объекту задания, могут быть убиты сразу, например, с помощью TerminateJobObject которых:

завершает все процессы, связанные с работой.

в пример C# в этом ответе (на основе этого ответа) использует JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE флаг вместо этого, который:

приводит к завершению всех процессов, связанных с заданием, при закрытии последнего дескриптора задания.


Как правильно закрыть Internet Explorer при запуске из PowerShell?

некоторые из тех, кто прокомментировал в приведенном выше потоке, что это вызвано ошибкой в Win7 (поскольку это, похоже, не происходит для пользователей, которые используют другие версии windows). Многие страницы в интернете, включая ошибку пользователя Microsoft page claim, и говорят вам просто использовать доступный метод quit на объекте IE, который должен закрыть все дочерние процессы (и, как сообщается делает в Win8 / XP и т. д.)

Я должен признать, со своей стороны, это была ошибка пользователя. Я нахожусь в win7, и причина, по которой метод quit не работал для меня, заключалась в ошибке в кодировании. А именно, я создавал объект IE при объявлении, а затем создавал другой (прикрепленный к тому же объекту) позже в коде... Я почти закончил взламывать рутину убийства родителей и детей, чтобы работать на меня, когда понял проблему.

из-за того, как IE функционирует, processID вы породили поскольку родитель может быть присоединен к другим windows / подпроцессам, которые вы не создали. Используйте quit и имейте в виду, что в зависимости от настроек пользователя (например, пустой кэш при выходе) для процессов может потребоваться несколько минут, чтобы закончить свои задачи и закрыть.