Запускать произвольные подпроцессы в Windows и до конца прекратить?

У меня есть приложение A, которое я хотел бы иметь возможность вызывать произвольные другие процессы, как указано пользователем в файле конфигурации.

Пакет script B - это один из таких процессов, который пользователь хотел бы вызывать A. B настраивает некоторые переменные среды, показывает некоторые сообщения и вызывает компилятор C для выполнения некоторой работы.

Предоставляется ли Windows стандартным способом для прекращения произвольных процессов? Предположим, что A запущен в консоли и получает CTRL + C. Может ли это передать это B и C? Предположим, что A работает в окне, и пользователь пытается закрыть окно, может ли он отменить B и C?

TerminateProcess - это вариант, но не очень хороший. Если A использует TerminateProcess на B, C продолжает работать. Это может вызвать неприятные проблемы, если C долго работает, поскольку мы можем запустить другой экземпляр C для работы с одними и теми же файлами, в то время как первый экземпляр C все еще работает тайно. Кроме того, TerminateProcess не приводит к чистому выходу.

GenerateConsoleCtrlEvent звучит неплохо и может работать, когда все работает в консоли, но в документации говорится, что вы можете отправлять CTRL + C только на свою собственную консоль, и поэтому не помогло бы, если бы A выполнялось в окне.

Есть ли эквивалент SIGINT в Windows? Мне бы очень хотелось найти статью, подобную этой: http://www.cons.org/cracauer/sigint.html для Windows.

Ответ 1

Я думаю, я немного опаздываю на этот вопрос, но я напишу что-нибудь в любом случае для тех, у кого такая же проблема.

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

Мне удалось решить это с помощью GenerateConsoleCtrlEvent(). Трудная часть состоит в том, что документация не совсем понятна, как именно она может быть использована, и подводные камни с ней.

Мое решение основано на том, что описано здесь. Но это также не объясняло все детали, так что вот подробности о том, как заставить его работать.

  • Создайте новое вспомогательное приложение "Helper.exe". Это приложение будет находиться между вашим приложением (родителем) и дочерним процессом, который вы хотите закрыть. Он также создаст фактический детский процесс. У вас должен быть этот процесс "средний человек" или сгенерирован GenerateConsoleCtrlEvent().

  • Используйте какой-то механизм IPC для связи от родителя к вспомогательному процессу, который помощник должен закрыть дочерний процесс. Когда помощник получает это событие, он вызывает "GenerateConsoleCtrlEvent (CTRL_BREAK, 0)", который закрывает сам себя и дочерний процесс. Я использовал объект события для этого сам, который родитель завершает, когда он хочет отменить дочерний процесс.

Чтобы создать ваш Helper.exe, создайте его с помощью CREATE_NO_WINDOW и CREATE_NEW_PROCESS_GROUP. И при создании дочернего процесса создайте его без флагов (0), то есть он выведет консоль из своего родителя. В противном случае это приведет к игнорированию события.

Очень важно, чтобы каждый шаг выполнялся следующим образом. Я пробовал всевозможные комбинации, но эта комбинация - единственная, которая работает. Вы не можете отправить событие CTRL_C. Это вернет успех, но процесс будет проигнорирован. CTRL_BREAK - единственный, который работает. Не имеет большого значения, так как в конце концов они вызовут ExitProcess().

Вы также не можете вызвать GenerateConsoleCtrlEvent() с идентификатором process groupd идентификатора дочернего процесса, который напрямую позволяет вспомогательному процессу продолжать жить. Это также не удастся.

Я потратил целый день, пытаясь заставить это работать. Это решение работает для меня, но если у кого-то есть что-то еще, добавьте, пожалуйста. Я пошел в другую сеть, найдя много людей с подобными проблемами, но не решительное решение проблемы. Как работает GenerateConsoleCtrlEvent(), это также немного странно, поэтому, если кто-то знает более подробные сведения об этом, делитесь им.

Ответ 2

Как сказал @Shakta, GenerateConsoleCtrlEvent() очень сложный, но вы можете отправить Ctrl + C без вспомогательного процесса.

void SendControlC(int pid)
{
    AttachConsole(pid); // attach to process console
    SetConsoleCtrlHandler(NULL, TRUE); // disable Control+C handling for our app
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // generate Control+C event
}