Подтвердить что ты не робот

Уничтожить дерево процессов программно в С#

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

4b9b3361

Ответ 1

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

/// <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

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

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

Ответ 2

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

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

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");

Ответ 3

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

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

$"pgrep -P {parentId}"

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

$"kill -TERM {processId}"

Ответ 4

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

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

Ответ 5

Если кто-то заинтересован, я взял один из ответов с другой страницы и немного изменил его. Теперь это самостоятельный класс со статическими методами. Он не имеет правильной обработки ошибок или ведения журнала. Модифицируйте, чтобы использовать для своих нужд. Предоставление вашего корневого процесса 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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

Ответ 9

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

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

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

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

Ответ 10

В .NET Core 3.0 есть метод только для этого, а именно новая перегрузка уже существующего метода Process.Kill(). Таким образом, выполнение process.Kill(true) для переменной process типа Process убивает процесс со всеми его потомками. Это кроссплатформенность, естественно.