Уничтожьте дерево процессов программно на C#
-
29-10-2019 - |
Вопрос
Я запускаю 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.Запустить?
Решение
У меня это сработало очень хорошо:
родовое словоОбновление 26.04.2016
Протестировано на Visual Studio 2015 Update 2
на Win7 x64
.По-прежнему работает так же хорошо, как и 3 года назад.
Обновление от 14 ноября 2017 г.
Добавлена проверка на наличие кода простоя системы, генерирующего код тега
Обновление 2018-03-02
Необходимо добавить ссылку на пространство имен if (pid == 0)
, см. комментарий от @MinimalTech ниже.Если у вас установлен ReSharper, он предложит сделать это за вас автоматически.
Обновление 2018-10-10
Наиболее распространенный вариант использования - уничтожение дочерних процессов, запущенных нашим собственным процессом C #.
В этом случае лучшим решением является использование вызовов Win32 в C #, чтобы сделать любой порожденный процесс дочерним процессом.Это означает, что при выходе из родительского процесса все дочерние процессы автоматически закрываются Windows, что устраняет необходимость в приведенном выше коде.Пожалуйста, дайте мне знать, если вы хотите, чтобы я опубликовал код.
Другие советы
Я не поклонник ни одного из представленных здесь решений.
Вот что я придумал:
родовое словоКак использовать:
родовое словоЕсли кому-то нужно решение ядра dotnet, dotnet cli предложил реализацию, основанную на taskill , как упомянуто выше, и рекурсивном pgrep / kill .для систем на базе unix. Можно найти полную реализациюна github .К сожалению, это внутренний класс, поэтому вам придется скопировать его в свою базу кода.
Список дочерних процессов (должно выполняться рекурсивно):
родовое словоЗавершить процесс:
родовое слово Вы должны вызвать Process.CloseMainWindow()
, который отправит сообщение в главное окно процесса.Думайте об этом как о том, что пользователь нажимает кнопку закрытия «X» или Файл | Выход из пункта меню.
Безопаснее послать в Internet Explorer сообщение о закрытии, чем пойти и убить все его процессы.Эти процессы могут делать что угодно, и вам нужно позволить IE сделать свое дело и завершить его, прежде чем просто убить его в середине выполнения чего-то, что может быть важно для будущих запусков.Это верно для любой программы, которую вы убиваете.
Если кому-то интересно, я взял один из ответов с другой страницы и немного изменил его.Теперь это автономный класс со статическими методами.В нем отсутствует надлежащая обработка ошибок или ведение журнала.Измените, чтобы использовать для своих нужд.Предоставление корневого процесса KillProcessTree сделает это.
родовое словоДругое решение - использовать команду taskill.Я использую в своих приложениях следующий код:
родовое словоВы используете IE8 или IE9?Из-за новой многопроцессорной архитектуры это, безусловно, запустило бы несколько процессов.В любом случае, взгляните на этот другойответ за получение дерева процессов и его уничтожение.
Другой подход, который может быть очень полезным, - это использование Windows API для объектов заданий . Объекту задания можно назначить процесс. Дочерние процессы такого процесса автоматически назначаются одному и тому же объекту задания.
Все процессы, назначенные объекту задания, могут быть остановлены сразу, например. с помощью TerminateJobObject , который :
<цитата>Завершает все процессы, связанные в данный момент с заданием.
Пример C # в этом ответе ( на основе этого ответа ) использует флаг JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE , который:
<цитата>Вызывает завершение всех процессов, связанных с заданием, при закрытии последнего дескриптора задания.
Как правильно закрыть Internet Explorer при запуске из PowerShell?
Некоторые из тех, кто прокомментировал в приведенной выше теме, что это вызвано ошибкой в Win7 (поскольку, похоже, это не встречается у пользователей, использующих другие версии Windows).Многие страницы в Интернете, включая страницу Microsoft, заявляют об ошибке пользователя и советуют вам просто использовать доступный метод quit для объекта IE, который также должен закрывать все дочерние процессы (и, как сообщается, делает это в Win8 / XP и т.д.)
Я должен признать, со своей стороны, что это БЫЛА ошибка пользователя.Я нахожусь в win7, и причина, по которой метод quit у меня не работал, заключалась в ошибке в кодировании.А именно, я создавал объект IE при объявлении, а затем создавал другой (прикрепленный к тому же объекту) позже в коде...Я почти закончил взламывать процедуру убийства родителей и детей, чтобы она работала на меня, когда осознал проблему.
Из-за того, как функционирует IE, ProcessId, который вы создали в качестве родительского, может быть привязан к другим окнам / подпроцессам, которые вы НЕ создавали.Используйте quit и имейте в виду, что в зависимости от пользовательских настроек (например, пустой кэш при выходе) процессам может потребоваться несколько минут, чтобы завершить свои задачи и закрыться.
В .NET Core 3.0 для этого есть метод, а именно новая перегрузка уже существующего метода Process.Kill()
.IOW, выполнение process.Kill(true)
для переменной process
типа Process
уничтожает процесс со всеми его потомками.Это, естественно, кроссплатформенный.