Отправить ctrl+c в процесс cmd.exe в С#

В настоящее время я пишу себе небольшую программу резервного копирования C#. Я использую стандартную форму Windows для интерфейса и вызываю cmd.exe как новый процесс, а затем использую XCOPY из этого нового процесса. Все работает отлично, за исключением последней функции, которую я хочу добавить, а именно возможности прерывания операции.

Из встроенной командной строки я могу сделать это аккуратно с помощью Ctrl + C, но, как я ни старался, я не могу воспроизвести эту функциональность, используя подход winforms и process. Я попытался перенаправить стандартный ввод и использовать его для отправки consolespecialkeys.ControlC в процесс, я также пытался отправить 0x03 и «/x03», оба из которых я читал в других сообщениях на форуме, являются шестнадцатеричным кодом для ctrl + c . Однако ничего из того, что я отправляю, не регистрируется, и выход из процесса убивает пользовательский интерфейс, но оставляет xcopy.exe работающим в фоновом режиме. Уничтожение xcopy.exe вручную приводит к тому, что копируемый файл остается наполовину скопированным и поврежденным, а не с помощью Ctrl + C в командной строке.

Я упускаю что-то ослепительно очевидное? Я новичок в C #, поэтому я подниму руки и признаю, что, скорее всего, я медленный или неправильно понимаю, как процесс работает с cmd.exe. Однако, поскольку процессы поддерживают стандартное перенаправление ввода, кажется, что это должно работать... по крайней мере, для меня. Ниже я представил основную схему своего кода на случай, если это поможет определить, где я ошибаюсь.

string XCopyArguments = "\"" + dir.FullName + "\" \"" + destination + "\" /D /S /I /E";  
Process XCopyProcess = new Process();  
ProcessStartInfo XCopyStartInfo = new ProcessStartInfo(); 
XCopyStartInfo.FileName = "CMD.exe ";  
XCopyStartInfo.RedirectStandardError = true;
XCopyStartInfo.RedirectStandardOutput = true;
XCopyStartInfo.RedirectStandardInput = true;
XCopyStartInfo.UseShellExecute = false;
XCopyStartInfo.CreateNoWindow = true;
XCopyStartInfo.Arguments = " /D /c XCOPY " + XCopyArguments;
XCopyProcess.EnableRaisingEvents = true;
XCopyProcess.StartInfo = XCopyStartInfo;
XCopyProcess.Start();                
XCopyProcess.WaitForExit(15000);
int ExitCode = XCopyProcess.ExitCode;
if (ExitCode > 0 & !XCopyProcess.HasExited)
{
XCopyProcess.Kill();
}
XCopyProcess.Dispose();

Заранее большое спасибо за любую помощь, которую кто-либо может предложить.


person Dante Invidia    schedule 05.05.2009    source источник


Ответы (7)


Я не хочу быть бессервиссером, но я думаю, что вам было бы намного лучше делать копирование внутри вашей программы. Используя File, Directory и другие классы в пространстве имен System.IO, это очень просто и дает вам полный контроль над отчетами о ходе выполнения, отменой операций и т. д.

person Tor Haugen    schedule 05.05.2009
comment
Большое спасибо за предложение. Честно говоря, я, вероятно, немного заморочился в своей решимости использовать XCOPY. Я начал использовать это для чистой простоты (вместо написания рекурсивных циклов foreach), но по мере того, как я начал добавлять больше слоев в свой проект, я думаю, что XCOPY становился все менее и менее эффективным способом справиться с работой. Сейчас я несколько начал работу над реальным механизмом копирования, используя .Net System.IO. Посмотрим, как я с этим справлюсь. Еще раз большое спасибо за участие и быстрый ответ. - person Dante Invidia; 06.05.2009

Да, делать операцию в .NET было бы проще. НО, мне также нужно отправить Ctrl-C в процесс, и у меня нет этой опции.

Итак, можем ли мы получить ответ на этот вопрос?

EDIT: Должен ли я опубликовать дубликат, чтобы получить ответ? И нет, @j0rd4n не ответил на вопрос.

person Nick Whaley    schedule 05.05.2009

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

Вот проблема с вашим примером:

XCopyStartInfo.CreateNoWindow = истина;

Установите для него значение false, и тогда он будет обрабатывать XCopyProcess.CloseMainWindow() и XCopyProcess.Close(). Гораздо чище, чем использование Kill().

person ruthy    schedule 08.12.2009

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

person Dan Byström    schedule 05.05.2009
comment
Как я сказал Тору Хаугену выше, я думаю, вы правы в том, что использование XCOPY, вероятно, немного нелогично на данном этапе проекта. Хотя тогда это казалось хорошей идеей. Спасибо за ваш вклад и предложение по альтернативному подходу, теперь я попробую этот подход. - person Dante Invidia; 06.05.2009

Извините, это в VB.NET.

Declare Function GenerateConsoleCtrlEvent Lib "kernel32" ( _
                    ByVal dwCtrlEvent As Integer, _
                    ByVal dwProcessGroupId As Integer _
                    ) As Integer

Private Const CTRL_C_EVENT As Integer = 0

Private Sub SendCtrlC()
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)

    ' send a Ctrl-C to this process
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, currentpid)

    ' send a Ctrl-C to the cmd process
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, cmdpid)
End Sub
person Community    schedule 19.05.2009
comment
Это не работает, если процесс запущен с CreateNoWindow=true. - person 3Dave; 17.08.2010

Я успешно отправил комбинацию CTRL-C процессу cmd.exe, созданному с помощью SW_HIDE, т. е. скрытому окну cmd.exe.

Техника заключалась в использовании EnumWindows для идентификации процесса и получения его дескриптора окна (у него все еще есть дескриптор для обработки сообщений, даже если он не виден).

Затем я использовал PostMessage, чтобы опубликовать в процессе комбинацию клавиш Ctrl-C. Это имело такой же эффект, как если бы пользователь нажал «ctrl-c», когда окно было активным.

Чтобы сделать это из C#, вы, вероятно, захотите посетить http://pinvoke.net/ — палочку-выручалочку, когда придет время для написания прототипов функций Win32 API на C#.

person Chris    schedule 23.01.2012

Вам придется вручную завершить процесс, если вы хотите справиться с копированием таким образом. В приведенном выше коде вы вызываете XCopyProcess.WaitForExit(...). Это блокирующий вызов, поэтому родительский процесс C# остановится в этой точке, пока дочерний процесс не завершится или не истечет временной интервал.

Что вы могли бы сделать, так это вместо блокировки вы можете спать в цикле, регулярно проверяя, запросил ли пользователь убить процесс через ваш пользовательский интерфейс C#. Если вы получаете это событие, вы явно завершаете процесс. В противном случае вы ждете еще один интервал, пока процесс не будет завершен.

EDIT: я согласен с другими комментариями. Копируйте напрямую из платформы .NET вместо использования xcopy.

person Jordan Parmer    schedule 05.05.2009
comment
Я задавался вопросом, не создает ли мне проблемы этот waitforexit, но между этим и упорным упорством в попытках отправить Ctrl + C в мой процесс, я никогда не предлагал решение, которое вы предложили. В конце концов, и в свете отзывов сообщества здесь, я думаю, что я все равно собираюсь отказаться от XCOPY и вместо этого использовать собственные возможности .Net, но все же большое спасибо за совет и за то, что нашли время ответить. Это очень ценится. - person Dante Invidia; 06.05.2009