Убить дерево процессов программно в C # - PullRequest
37 голосов
/ 05 мая 2011

Я запускаю 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();

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

Ответы [ 9 ]

59 голосов
/ 01 мая 2012

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

/// <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 на Win7 x64. Работает так же хорошо, как и 3 года назад.

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

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

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

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

Обновление 2018-10-10

Наиболее распространенный вариант использования этого - уничтожение любых дочерних процессов, запущенных нашим собственным процессом C #.

В этом случае лучшим решением будет использование вызовов Win32 в C #, чтобы сделать любой порожденный процесс дочерним процессом. Это означает, что при выходе из родительского процесса все дочерние процессы автоматически закрываются Windows, что устраняет необходимость в приведенном выше коде. Пожалуйста, дайте мне знать, если вы хотите, чтобы я отправил код.

11 голосов
/ 20 февраля 2017

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

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

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");
10 голосов
/ 14 ноября 2012

Вы должны позвонить Process.CloseMainWindow(), который отправит сообщение в главное окно процесса. Представьте, что пользователь нажимает кнопку закрытия «X» или Файл | Выход пункт меню.

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

8 голосов
/ 14 августа 2017

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

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

$"pgrep -P {parentId}"

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

$"kill -TERM {processId}"
7 голосов
/ 07 мая 2011

Если кому-то интересно, я взял один из ответов с другой страницы и немного его изменил. Теперь это автономный класс со статическими методами. не имеет надлежащую обработку ошибок или ведение журнала. Изменить для использования для ваших собственных нужд. Предоставление вашего корневого процесса для 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);
    }
}
6 голосов
/ 15 сентября 2015

Другое решение - использовать команду 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 { }
}
4 голосов
/ 05 мая 2011

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

2 голосов
/ 17 мая 2017

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

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

Завершает все процессы, связанные с заданием.

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

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

0 голосов
/ 20 ноября 2015

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

Некоторые из тех, кто прокомментировал в вышеприведенном обсуждении, что это вызвано ошибкой в ​​Win7 (так как кажется, чтопроисходят для пользователей, которые используют другие версии Windows).На многих страницах в Интернете, в том числе на странице Microsoft, утверждается, что произошла ошибка пользователя, и вам нужно просто использовать доступный метод quit для объекта IE, который ПРЕДЛАГАЕТСЯ также закрыть все дочерние процессы (и, как сообщается, в Win8 / XP и т. Д.)

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

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

...