Могу ли я отправить ctrl-C (SIGINT) в приложение на Windows?
У меня есть (в прошлом) написанные кросс-платформенные (Windows/Unix) приложения, которые при запуске из командной строки обрабатывали пользовательский тип Ctrl -C комбинация таким же образом (т. е. для завершения приложения чисто).
возможно ли в Windows отправить Ctrl -C/SIGINT / эквивалентно процессу из другого (несвязанного) процесса, чтобы запросить, чтобы он закончился чисто (давая ему возможность привести в порядок ресурсов и т. д.)?
12 ответов:
самое близкое, что я пришел к решению-это SendSignal 3rd party app. Автор перечисляет исходный код и исполняемый файл. Я проверил, что он работает под 64-разрядной windows (работает как 32-разрядная программа, убивая другую 32-разрядную программу), но я не понял, как встроить код в программу windows (либо 32-разрядную, либо 64-разрядную).
Как работает:
после долгих поисков в отладчике я обнаружил, что точка входа, которая на самом деле поведение, связанное с сигналом, таким как ctrl-break, является kernel32!CtrlRoutine. Функция имела тот же прототип, что и ThreadProc, поэтому ее можно использовать с CreateRemoteThread напрямую, без необходимости вводить код. Однако это не экспортированный символ! Он находится по разным адресам (и даже имеет разные имена) в разных версиях Windows. Что же делать?
вот решение, которое я наконец придумал. Я устанавливаю обработчик ctrl консоли для своего приложения, а затем создаю ctrl-сигнал разрыва для моего приложения. Когда мой обработчик вызывается, я оглядываюсь на верхнюю часть стека, чтобы узнать параметры, переданные в kernel32!BaseThreadStart. Я беру первый param, который является желаемым начальным адресом потока, который является адресом kernel32!CtrlRoutine. Затем я возвращаюсь из своего обработчика, указывая, что я обработал сигнал, и мое приложение не должно быть прекращено. Вернувшись в основной поток, я жду, пока адрес kernel32!CtrlRoutine был извлечен. Как только я получу это, я создаю удаленный поток в целевом процессе с обнаруженным стартовым адресом. Это приводит к тому, что обработчики ctrl в целевом процессе оцениваются так, как если бы ctrl-break был нажат!
хорошая вещь заключается в том, что затрагивается только целевой процесс, и любой процесс (даже оконный процесс) может быть нацелен. Одним из недостатков является то, что мое маленькое приложение не может быть использовано в пакетном файле, так как оно убьет его, когда оно отправит событие ctrl-break, чтобы обнаружить адрес на kernel32!CtrlRoutine.
(перед ним
startЕсли запустить его в пакетный файл.)
Я провел некоторые исследования по этой теме, которые оказались более популярными, чем я ожидал. Ответ KindDragon был одним из поворотных пунктов.
Я написал больше в блоге по этой теме и создана рабочая демонстрационная программа, которая демонстрирует использование этого типа системы для закрытия приложения командной строки в нескольких приятных модах. В этом посте также перечислены внешние ссылки, которые я использовал в своем исследовании.
короче говоря, эти демо-программы делают следующее:
- запустить программу с видимое окно, используя .Чистая, скрывает при использовании PInvoke, выполнено за 6 секунд, показать при использовании PInvoke, остановка .Нет.
- запустите программу без окна с помощью .Net, запустите в течение 6 секунд, остановитесь, подключив консоль и выдав ConsoleCtrlEvent
Edit: исправленное решение от KindDragon для тех, кто интересуется кодом здесь и сейчас. Если вы планируете запускать другие программы после остановки во-первых, вы должны повторно включить обработку Ctrl-C, иначе следующий процесс унаследует отключенное состояние родителя и не будет отвечать на Ctrl-C.
[DllImport("kernel32.dll", SetLastError = true)] static extern bool AttachConsole(uint dwProcessId); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern bool FreeConsole(); // Enumerated type for the control messages sent to the handler routine enum CtrlTypes : uint { CTRL_C_EVENT = 0, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT = 5, CTRL_SHUTDOWN_EVENT } [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId); public void StopProgram(Process proc) { //This does not require the console window to be visible. if (AttachConsole((uint)proc.Id)) { // Disable Ctrl-C handling for our program SetConsoleCtrlHandler(null, true); GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0); // Must wait here. If we don't and re-enable Ctrl-C // handling below too fast, we might terminate ourselves. proc.WaitForExit(2000); FreeConsole(); //Re-enable Ctrl-C handling or any subsequently started //programs will inherit the disabled state. SetConsoleCtrlHandler(null, false); } }кроме того, план для решения чрезвычайных, если
AttachConsole()или отправленный сигнал должен потерпеть неудачу, например спать тогда это:if (!proc.HasExited) { try { proc.Kill(); } catch (InvalidOperationException e){} }
Я думаю, что я немного опоздал на этот вопрос, но я все равно напишу что-нибудь для тех, у кого есть такая же проблема. Это тот же ответ, что и я дал этой вопрос.
моя проблема заключалась в том, что я хотел бы, чтобы мое приложение было графическим приложением, но выполняемые процессы должны выполняться в фоновом режиме без какого-либо интерактивного окна консоли. Я думаю, что это решение должно также работать, когда родительский процесс-это процесс консоли. Возможно, вам придется удалить Однако флаг "CREATE_NO_WINDOW".
мне удалось решить эту проблему с помощью GenerateConsoleCtrlEvent() С приложением фантик. Хитрая часть заключается в том, что документация не совсем ясно, как именно она может быть использована и подводные камни с ним.
мое решение основано на том, что описано здесь. Но это на самом деле не объясняло все детали и с ошибкой, так что вот подробности о том, как заставить его работать.
создать новый вспомогательное приложение "Помощник.исполняемый." Это приложение будет сидеть между вашим приложением (родителем) и дочерним процессом, который вы хотите закрыть. Это также создаст фактический дочерний процесс. У вас должен быть этот процесс "среднего человека" или GenerateConsoleCtrlEvent() потерпит неудачу.
используйте какой-то механизм IPC для передачи от родителя к вспомогательному процессу, что помощник должен закрыть дочерний процесс. Когда помощник получает это событие, он вызывает " GenerateConsoleCtrlEvent(CTRL_BREAK, 0) " который закрывает себя и дочерний процесс. Я использовал объект события для этого сам, который родитель завершает, когда он хочет отменить дочерний процесс.
для создания вашего помощника.exe создать его с CREATE_NO_WINDOW и CREATE_NEW_PROCESS_GROUP. И при создании дочернего процесса создайте его без флагов (0), что означает, что он будет получать консоль от своего родителя. В противном случае это приведет к игнорированию события.
очень важно, чтобы каждый шаг был сделан вроде этого. Я пробовал все различные виды комбинаций, но эта комбинация является единственной, которая работает. Вы не можете отправить событие CTRL_C. Он вернет успех, но будет проигнорирован процессом. CTRL_BREAK является единственным, который работает. Не имеет значения, так как они оба вызова ExitProcess() в конце.
вы также не можете вызвать GenerateConsoleCtrlEvent() с идентификатором процесса groupd идентификатора дочернего процесса непосредственно позволяя вспомогательному процессу продолжать жить. Эта воля также терпеть неудачу.
Я потратил целый день, пытаясь получить эту работу. Это решение работает для меня, но если у кого есть что-нибудь добавить, пожалуйста. Я обошел всю сеть, находя много людей с похожими проблемами, но не нашел определенного решения проблемы. Как GenerateConsoleCtrlEvent () работает также немного странно, так что если кто-нибудь знает более подробную информацию об этом, пожалуйста, поделитесь.
Edit:
для графического приложения "обычный" способ справиться с этим в разработке Windows-это отправить сообщение WM_CLOSE в Главное окно процесса.
для консольного приложения необходимо использовать SetConsoleCtrlHandler добавить
CTRL_C_EVENT.Если приложение не соблюдает это, вы можете позвонить TerminateProcess.
как-то
GenerateConsoleCtrlEvent()возвращает ошибку, если вы вызываете ее для другого процесса, но вы можете подключиться к другому консольному приложению и отправить событие всем дочерним процессам.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 }
Это должно быть кристально ясно, потому что на данный момент это не так. существует измененная и скомпилированная версия SendSignal для отправки Ctrl-C (по умолчанию он отправляет только Ctrl+Break). Вот некоторые двоичные файлы:
(2014-3-7) : я построил как 32-разрядную, так и 64-разрядную версию с Ctrl-C, она называется SendSignalCtrlC.exe и вы можете скачать его по адресу:https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86/SendSignalCtrlC.exe https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86_64/SendSignalCtrlC.exe -- Юрай Михалак
Я также отразил эти файлы на всякий случай:
32-разрядная версия:https://www.dropbox.com/s/r96jxglhkm4sjz2/SendSignalCtrlC.exe?dl=0
64-разрядная версия:https://www.dropbox.com/s/hhe0io7mcgcle1c/SendSignalCtrlC64.exe?dl=0отказ от ответственности: я не создавал эти файлы. Никаких изменений не было сделано в скомпилированном виде исходный файл. Единственной проверенной платформой является 64-разрядная Windows 7. Рекомендуется адаптировать источник, доступный по адресу http://www.latenighthacking.com/projects/2003/sendSignal/ и скомпилировать его самостоятельно.
вот код, который я использую в моем приложении C++.
положительные моменты :
- работает из консольного приложения
- работает из службы Windows
- задержка не требуется
- не закрывает текущее приложение
отрицательные моменты :
- главная консоль потеряна и создана новая (см. FreeConsole)
- переключение консоли дают странные результаты...
// Inspired from http://stackoverflow.com/a/15281070/1529139 // and http://stackoverflow.com/q/40059902/1529139 bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent) { bool success = false; DWORD thisConsoleId = GetCurrentProcessId(); // Leave current console if it exists // (otherwise AttachConsole will return ERROR_ACCESS_DENIED) bool consoleDetached = (FreeConsole() != FALSE); if (AttachConsole(dwProcessId) != FALSE) { // Add a fake Ctrl-C handler for avoid instant kill is this console // WARNING: do not revert it or current program will be also killed SetConsoleCtrlHandler(nullptr, true); success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE); FreeConsole(); } if (consoleDetached) { // Create a new console if previous was deleted by OS if (AttachConsole(thisConsoleId) == FALSE) { int errorCode = GetLastError(); if (errorCode == 31) // 31=ERROR_GEN_FAILURE { AllocConsole(); } } } return success; }пример использования :
DWORD dwProcessId = ...; if (signalCtrl(dwProcessId, CTRL_C_EVENT)) { cout << "Signal sent" << endl; }
void SendSIGINT( HANDLE hProcess ) { DWORD pid = GetProcessId(hProcess); FreeConsole(); if (AttachConsole(pid)) { // Disable Ctrl-C handling for our program SetConsoleCtrlHandler(NULL, true); GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // SIGINT //Re-enable Ctrl-C handling or any subsequently started //programs will inherit the disabled state. SetConsoleCtrlHandler(NULL, false); WaitForSingleObject(hProcess, 10000); } }
основанный на идентификатор процесса, можно послать сигнал завершения или принудительно изящно или любой другой сигнал.
Список всех процессов :
C:\>tasklistубить процесс:
C:\>Taskkill /IM firefox.exe /F or C:\>Taskkill /PID 26356 /Fдетали:
http://tweaks.com/windows/39559/kill-processes-from-command-prompt/
в Java, используя JNA с ядром 32.библиотека dll, аналогичная решению на C++. Запускает метод CtrlCSender main как процесс, который просто получает консоль процесса для отправки события Ctrl+C и генерирует событие. Поскольку он работает отдельно без консоли, событие Ctrl+C не нужно отключать и снова включать.
CtrlCSender.java - на основании Nemo1024 это и KindDragon это!--8--> ответы.
учитывая известное идентификатор процесса, это приложение consoless будет приложить консоли целенаправленный процесс и создать сочетание клавиш CTRL+C на это событие.
import com.sun.jna.platform.win32.Kernel32; public class CtrlCSender { public static void main(String args[]) { int processId = Integer.parseInt(args[0]); Kernel32.INSTANCE.AttachConsole(processId); Kernel32.INSTANCE.GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0); } }Главная Приложения - запускает CtrlCSender как отдельный консольный процесс
ProcessBuilder pb = new ProcessBuilder(); pb.command("javaw", "-cp", System.getProperty("java.class.path", "."), CtrlCSender.class.getName(), processId); pb.redirectErrorStream(); pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); pb.redirectError(ProcessBuilder.Redirect.INHERIT); Process ctrlCProcess = pb.start(); ctrlCProcess.waitFor();
мой друг предложил совершенно другой способ решения проблемы, и это сработало для меня. Используйте vbscript, как показано ниже. Он запускается и приложение, пусть он работает в течение 7 секунд и закройте его с помощью ctrl+c.
' Пример VBScript
Set WshShell = WScript.CreateObject("WScript.Shell") WshShell.Run "notepad.exe" WshShell.AppActivate "notepad" WScript.Sleep 7000 WshShell.SendKeys "^C"
Я нашел все это слишком сложно и использовать SendKeys отправить CTRL -C нажатие клавиши в окне командной строки (т. е. cmd.exe window) В качестве обходного пути.
Comments