Могу ли я послать Ctrl-C (SIGINT) в приложение на Windows?

голоса
73

Я (в прошлом) написал кросс-платформенный (Windows / Unix) приложения , которые при запуске из командной строки, обрабатываемых пользователь напечатал Ctrl- Cсочетание таким же образом (то есть прекратить применение чисто).

Возможно ли на Windows , чтобы отправить Ctrl- C/ SIGINT / эквивалентного процесс от другого (несвязанного) процесса требовать , чтобы он прекратить чисто (давая ему возможность привести в порядок ресурсов и т.д.)?

Задан 01/05/2009 в 21:18
источник пользователем
На других языках...                            


13 ответов

голоса
45

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

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

Короче говоря, эти демонстрационные программы выполните следующие действия:

  • Запуск программы с видимым окном с помощью .Net, скрытие с PInvoke, работать в течение 6 секунд, показывают с PInvoke, остановка с .Net.
  • Запуск программы без окна с использованием .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){}
}
Ответил 07/03/2013 в 21:27
источник пользователем

голоса
26

Ближайшим , что я пришел к решению является SendSignal третьего приложения партии. Списки автора исходного кода и исполняемым. Я подтвердил , что он работает под 64-битные окнами (работают как 32-битная программа, убивая еще 32-разрядную программу), но я не понял, как вставлять код в виде окна программы (или 32-разрядный или 64 бит).

Как это работает:

После того, как много копаться в отладчике я обнаружил, что точка входа, что на самом деле делает поведение, связанное с сигналом, как Ctrl-Break является kernel32! CtrlRoutine. Функция имела тот же прототип, как ThreadProc, поэтому он может быть использован с CreateRemoteThread напрямую, без необходимости вводить код. Однако, это не экспортируемый символ! Это по разным адресам (и даже имеет разные названия) на разных версиях Windows. Что делать?

Вот решение, которое я наконец придумал. Установить обработчик CTRl консоли для моего приложения, а затем генерировать сигнал Ctrl-Break для моего приложения. Когда мой обработчик вызывается, я оглядываюсь на вершине стека, чтобы узнать параметры, передаваемые kernel32! BaseThreadStart. Я хватаю первые из параметров, который желаемый адрес начала резьбы, которая является адресом kernel32! CtrlRoutine. Затем я возвращаюсь из моего обработчика, указывая, что я обработал сигнал и мое приложение не должно быть прекращено. Еще в главном потоке, я жду, пока адрес kernel32! CtrlRoutine не был получен. После того, как я получил его, я создать удаленный поток в целевом процессе с обнаруженным начальным адресом. Это приводит к тому, что обработчики Упр в целевом процессе должны быть оценены, как если нажать Ctrl-Break была нажата!

Хорошая вещь, что только целевой процесс влияет, и любой процесс (даже оконном процесс) может быть целенаправленным. Одним из недостатков является то, что мое маленькое приложение не может быть использована в пакетном файле, так как он будет убивать его, когда он посылает событие Ctrl-Break, чтобы открыть адрес kernel32! CtrlRoutine.

(Перед ним , startесли запустить его в пакетном файле.)

Ответил 24/07/2009 в 18:46
источник пользователем

голоса
16

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

Моя проблема заключалась в том, что я хотел бы мое приложение, чтобы быть приложением с графическим интерфейсом, но процессы, выполняемые должны работать в фоновом режиме без какого-либо интерактивного окна консоли прилагается. Я думаю, что это решение должно работать, если родительский процесс представляет собой консольный процесс. Вы, возможно, придется удалить «CREATE_NO_WINDOW» флаг, хотя.

Мне удалось решить это с помощью 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 () с процессом groupd идентификатор дочернего процесса ид непосредственно позволяя процесс хелперов продолжать жить. Это будет не в состоянии, а также.

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

Ответил 15/03/2010 в 08:58
источник пользователем

голоса
6

Каким - то образом 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
}
Ответил 15/10/2012 в 16:43
источник пользователем

голоса
6

Редактировать:

Для графического интерфейса приложения, «нормальный» способ справиться с этим в развитии ОС Windows будет посылать сообщение WM_CLOSE в главное окно процесса.

Для консольного приложения, вам нужно использовать SetConsoleCtrlHandler добавитьCTRL_C_EVENT .

Если приложение не почитает , что вы могли бы назвать TerminateProcess .

Ответил 01/05/2009 в 21:23
источник пользователем

голоса
3

Вот код, который я использую в моем приложении 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;
}
Ответил 04/01/2017 в 10:04
источник пользователем

голоса
3

Он должен быть кристально чистым , потому что на данный момент это не так . Существует модифицированная и скомпилированная версия 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 - Юрай Michalak

Я также зеркальный эти файлы только в случае:
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/ и скомпилировать его самостоятельно.

Ответил 26/03/2015 в 12:11
источник пользователем

голоса
2
        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);
            }
        }
Ответил 25/04/2017 в 10:26
источник пользователем

голоса
2

В Java, используя ЮНА с библиотекой Kernel32.dll, аналогично решению C ++. Запускает основной метод CtrlCSender как процесс, который только получает консоль процесса, чтобы отправить событие Ctrl + C, чтобы и генерирует событие. Как он работает отдельно без консоли событие Ctrl + C не должно быть отключено и снова включено.

CtrlCSender.java - на основе Nemo1024 - х и KindDragon - х ответах.

Учитывая известный идентификатор процесса, это 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 как отдельный процесс consoless

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();
Ответил 16/03/2017 в 16:30
источник пользователем

голоса
1

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

Перечислить весь процесс:

C:\>tasklist

Для того, чтобы убить процесс:

C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F

Детали:

http://tweaks.com/windows/39559/kill-processes-from-command-prompt/

Ответил 03/02/2017 в 10:41
источник пользователем

голоса
1

Один мой знакомый предложил совершенно иной путь решения проблемы , и он работал на меня. Используйте VBScript , как показано ниже. Она начинается и приложение, дайте ему поработать в течение 7 секунд и закрыть его с помощью Ctrl + C .

«VBScript Пример

Set WshShell = WScript.CreateObject("WScript.Shell")

WshShell.Run "notepad.exe"

WshShell.AppActivate "notepad"

WScript.Sleep 7000

WshShell.SendKeys "^C"
Ответил 22/01/2015 в 20:45
источник пользователем

голоса
1

Я нашел все это слишком сложно и использовать SendKeys для отправки CTRL- Cнажатие клавиши в окне командной строки (то есть cmd.exe окно) в качестве обходного пути.

Ответил 11/02/2014 в 12:01
источник пользователем

голоса
0

Да. windows-killПроект делает именно то , что вы хотите:

windows-kill -SIGINT 1234
Ответил 02/04/2019 в 03:26
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more