Несмотря на тот факт, что использование GenerateConsoleCtrlEvent для отправки сигнала Ctrl + C является правильным ответом, для его работы в различных типах приложений .NET требуется значительное уточнение.
Если ваше .NET-приложение не использует собственную консоль (WinForms / WPF / Windows Service / ASP.NET), основной поток будет:
- Присоедините основной .NET-процесс к консоли процесса, который вы хотите, нажав Ctrl + C
- Предотвращение остановки основного процесса .NET из-за события Ctrl + C с SetConsoleCtrlHandler
- Создание события консоли для текущей консоли с GenerateConsoleCtrlEvent (processGroupId должен быть равен нулю! Ответ с кодом, который отправляет p.SessionId, не будет работать и будет неправильным)
- Отключение от консоли и восстановление обработки Ctrl + C основным процессом
Следующий фрагмент кода иллюстрирует, как это сделать:
Process p;
if (AttachConsole((uint)p.Id)) {
SetConsoleCtrlHandler(null, true);
try {
if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT,0))
return false;
p.WaitForExit();
} finally {
FreeConsole();
SetConsoleCtrlHandler(null, false);
}
return true;
}
где SetConsoleCtrlHandler, FreeConsole, AttachConsole и GenerateConsoleCtrlEvent являются собственными методами WinAPI:
internal const int CTRL_C_EVENT = 0;
[DllImport("kernel32.dll")]
internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
internal static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH
delegate Boolean ConsoleCtrlDelegate(uint CtrlType);
Все становится сложнее, если вам нужно отправить Ctrl + C из консольного приложения .NET. Подход не будет работать, потому что AttachConsole возвращает false в этом случае (главное приложение консоли уже имеет консоль). Можно вызвать FreeConsole до вызова AttachConsole, но в результате оригинальная консоль приложения .NET будет потеряна, что в большинстве случаев неприемлемо.
Мое решение для этого случая (которое действительно работает и не имеет побочных эффектов для консоли основного процесса .NET):
- Создать небольшую вспомогательную консольную программу .NET, которая принимает идентификатор процесса из аргументов командной строки, теряет свою собственную консоль с FreeConsole перед вызовом AttachConsole и отправляет Ctrl + C целевому процессу с кодом, упомянутым выше
- Основной консольный процесс .NET просто вызывает эту утилиту в новом процессе, когда ему нужно отправить Ctrl + C другому консольному процессу