Как отправить ctrl + c процессу в С#?
Я пишу класс оболочки для исполняемого файла командной строки. Этот exe принимает ввод от stdin, пока я не нажму ctrl + c в командной строке командной оболочки, и в этом случае он выводит результат на основе ввода в stdout. Я хочу, чтобы имитировать, что ctrl + c нажмите в коде С#, отправив команду kill в объект процесса. Net. Я пробовал вызывать Process.kill(), но это, похоже, не дает мне ничего в этом процессе StandardOutput StreamReader. Может быть, я ничего не сделаю? Вот код, который я пытаюсь использовать:
ProcessStartInfo info = new ProcessStartInfo(exe, args);
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
p.StandardInput.AutoFlush = true;
p.StandardInput.WriteLine(scriptcode);
p.Kill();
string error = p.StandardError.ReadToEnd();
if (!String.IsNullOrEmpty(error))
{
throw new Exception(error);
}
string output = p.StandardOutput.ReadToEnd();
однако вывод всегда пуст, хотя я возвращаю данные из stdout, когда я запускаю exe вручную.
edit: это С# 2.0 btw
Ответы
Ответ 1
Я только что понял ответ. Спасибо вам за ваши ответы, но, оказывается, все, что я должен был сделать, это следующее:
p.StandardInput.Close()
который вызывает программу, которую я породил, чтобы закончить чтение из stdin и вывести то, что мне нужно.
Ответ 2
@alonl: пользователь пытается обернуть программу командной строки. Программы командной строки не имеют сообщений, если они специально не созданы, и даже если это так, Ctrl + C не имеет той же семантики в приложении Windows-среды (копия по умолчанию), как это делается в среда командной строки (Break).
Я бросил это вместе. CtrlCClient.exe просто вызывает Console.ReadLine() и ждет:
static void Main(string[] args)
{
ProcessStartInfo psi = new ProcessStartInfo("CtrlCClient.exe");
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process proc = Process.Start(psi);
Console.WriteLine("{0} is active: {1}", proc.Id, !proc.HasExited);
proc.StandardInput.WriteLine("\x3");
Console.WriteLine(proc.StandardOutput.ReadToEnd());
Console.WriteLine("{0} is active: {1}", proc.Id, !proc.HasExited);
Console.ReadLine();
}
Мой вывод, похоже, делает то, что вы хотите:
4080 is active: True
4080 is active: False
Надеюсь, что это поможет!
(Чтобы уточнить:\x3 - это шестнадцатеричная escape-последовательность для шестнадцатеричного символа 3, которая является ctrl + c. Это не просто магическое число.;))
Ответ 3
Несмотря на то, что использование 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 в другой процесс консоли.
Ответ 4
Хорошо, вот решение.
Способ отправки сигнала Ctrl-C - это GenerateConsoleCtrlEvent. ОДНАКО, этот вызов принимает параметр processGroupdID и отправляет сигнал Ctrl-C всем процессам в группе. Это было бы неплохо, если бы не тот факт, что нет никакого дочернего процесса spawn в .net, который находится в другой группе процессов, чем вы (родительский). Таким образом, когда вы отправляете GenerateConsoleCtrlEvent, И ВЫ (РОДИТЕЛЯ) ПОЛУЧИТЕ ЭТО. Таким образом, вам нужно также зафиксировать событие ctrl-c в родительском элементе, а затем определить, не стоит ли игнорировать его.
В моем случае я хочу, чтобы родитель мог обрабатывать также события Ctrl-C, поэтому мне нужно раскрыть между событиями Ctrl-C, отправленными пользователем на консоли, и те, которые отправлены родительским процессом дочернему, Я делаю это, просто взломав установку/снятие булевского флага, отправляя ctrl-c дочернему элементу, а затем проверяя этот флаг в родительском обработчике ctrl-c (т.е. При отправке ctrl-c в child, а затем игнорируем. )
Итак, код будет выглядеть примерно так:
//import in the declaration for GenerateConsoleCtrlEvent
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool GenerateConsoleCtrlEvent(ConsoleCtrlEvent sigevent, int dwProcessGroupId);
public enum ConsoleCtrlEvent
{
CTRL_C = 0,
CTRL_BREAK = 1,
CTRL_CLOSE = 2,
CTRL_LOGOFF = 5,
CTRL_SHUTDOWN = 6
}
//set up the parents CtrlC event handler, so we can ignore the event while sending to the child
public static volatile bool SENDING_CTRL_C_TO_CHILD = false;
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
e.Cancel = SENDING_CTRL_C_TO_CHILD;
}
//the main method..
static int Main(string[] args)
{
//hook up the event handler in the parent
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
//spawn some child process
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.Arguments = "childProcess.exe";
Process p = new Process();
p.StartInfo = psi;
p.Start();
//sned the ctrl-c to the process group (the parent will get it too!)
SENDING_CTRL_C_TO_CHILD = true;
GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, p.SessionId);
p.WaitForExit();
SENDING_CTRL_C_TO_CHILD = false;
//note that the ctrl-c event will get called on the parent on background thread
//so you need to be sure the parent has handled and checked SENDING_CTRL_C_TO_CHILD
already before setting it to false. 1000 ways to do this, obviously.
//get out....
return 0;
}
Ответ 5
Попробуйте отправить комбинацию клавиш Ctrl + C вместо прямого завершения процесса:
[DllImport("user32.dll")]
public static extern int SendMessage(
int hWnd, // handle to destination window
uint Msg, // message
long wParam, // first message parameter
long lParam // second message parameter
);
Посмотрите на MSDN, вы должны найти то, что вам нужно, чтобы отправить комбинацию Ctrl + Key...
Я знаю, что сообщение, которое вам нужно для отправки Alt + Key, это WM_SYSTEMKEYDOWN и WM_SYSTEMKEYUP, не могу сказать вам о Ctrl...