Возникли проблемы с классом процесса при перенаправлении вывода командной строки в winform

#c# #winforms #process #command-prompt

#c# #winforms #процесс #командная строка

Вопрос:

Я искал на рынке приложение командной строки с вкладками, так как мне надоело, что несколько окон командной строки загромождают один из экранов моего рабочего стола, когда я подумал о возможности создания собственного. Хотя я понимаю, что это будет не так здорово, как отдельный продукт, но я думаю, что будет хорошим упражнением создать свой собственный, просто чтобы лучше познакомиться с «Системой.Диагностические » классы (Process, ProcessStartInfo и т. Д.), С которыми я никогда раньше не играл.

Я почти только что создал простое приложение winforms, в котором есть 2 вкладки (которые содержат richtextfields), текстовое поле (для ввода команды) и кнопка (для фактического запуска команды).

Я нашел различные сообщения, в которых показано, как выполнить команду, но у меня возникают проблемы с фактическим выполнением команды и возвратом результатов в richtextbox. Вот метод, который я создал на основе информации, которую я знаю на данный момент: (Обновленная база на основе предыдущих ответов)

 public void GetConsoleOuput(string command)
{
  string outputString;

  ProcessStartInfo startupInfo = new ProcessStartInfo()
  startInfo.FileName = "cmd.exe";
  startInfo.RedirectStandardOuput = true;
  startInfo.WindowStyle = ProcessWindowStyle.Hidden:
  startInfo.UseShellExecute = false;

  startInfo.Arguments("/C "   command);

  Process process = new Process()
  process.StartInfo = startInfo;
  process.OutputDataReceived  = new DataReceivedEventHandler(AppendRichBoxText);

  process.Start()
  process.BeginOutputReadLine();

  process.WaitForExit();
  process.Close();
}

public void AppendRichBoxTet(object sender, DataReceivedEventArgs args)
{
  string outputString = args.Data;

  // need to have the richTextBox updated using it's own thread
  richTextBox.BeginInvoke( /* not sure what to put inside here */);
}
  

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

На данный момент я застрял на том, как выполнить метод BeginInvoke, чтобы этот RichTextBox обновлял свой текст в своем собственном потоке.

Комментарии:

1. Я использую screen.exe от cygwin, но я не знаю, насколько хорошо это будет работать с cmd.exe

Ответ №1:

Да, вы можете. Вместо использования ReadToEnd (которое будет блокироваться до завершения процесса) вы можете использовать API перенаправления вывода, управляемого событиями:

  • Установите RedirectStandardOutput , как вы уже делаете
  • Запустите процесс
  • Вызов BeginOutputReadLine
  • Подпишитесь на OutputDataReceived событие и используйте Control.BeginInvoke для маршалирования обратно в поток пользовательского интерфейса для добавления в RichTextBox .

В BeginOutputReadLine документации есть пример.

РЕДАКТИРОВАТЬ: для Control.BeginInvoke , это, вероятно, было бы самым простым решением:

 public void AppendRichBoxTet(object sender, DataReceivedEventArgs args)
{
  string outputString = args.Data;
  MethodInvoker append = () => richTextBox.AppendText(outputString);
  richTextBox.BeginInvoke(append);
}
  

Комментарии:

1. Есть ли фактический элемент управления. Метод BeginInvoke? Или мне вызвать BeginInvoke для фактического элемента управления, который я хочу обновить (в данном случае RichTextBox в методе eventhandler)? Кроме того, после того, как я подпишусь на событие и предоставлю этот асинхронный вызов для обновления окна richtext, что еще мне нужно в коде «process» между вызовом process . BeginOutputReadLine а затем process . Закрыть ()? В приведенном примере это довольно просто визуализировать, поскольку он фактически создает Streamwriter . Кроме того, должен ли я подписаться на событие OutputDataReceived до или после процесса. Метод Start()?

2. @HansGruber: Вы вызываете его в элементе управления — он объявлен в Control , но это метод экземпляра. Вы должны иметь возможность просто запустить процесс, не закрывая его явно — вы можете сделать это в обработчике для завершенного события, если хотите. Вы должны иметь возможность немедленно подписаться на событие OutputDataReceived, согласно примеру в документации.

3. Я думаю, в чем моя проблема, так это в том, что я все еще не уверен, как точно все подключить. Я добавил «процесс. OutputDataReceived = new DataReceivedEventHandler(ShowConsoleStuff);» сразу после запуска процесса. Я предполагаю, что каждый раз, когда срабатывает событие OutputDataReceived, оно должно выполнять код в методе с именем «ShowConsoleStuff», который я создал. Внутри этого метода я должен выполнить метод richTextBox1.BeginInvoke()? И если это так, означает ли это, что я должен создать делегат, который просто принимает то же текстовое поле и вызывает AppendText?

4. @HansGruber: Да, в основном это так. Однако в этом вам поможет лямбда-выражение.

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

Ответ №2:

Здесь есть несколько ошибок

1) вы не ждете завершения процесса. Вы должны использовать методы ожидания, чтобы дождаться, пока это будет сделано, прежде чем читать вывод.

2) Выходной буфер имеет определенный размер, и если он переполнен, вы попадете в тупик. Обязательно добавьте обработчик событий для чтения std, когда он доступен, или запустите другой поток, чтобы периодически проверять его.

3) Почему вы используете cmd.exe вообще?

Комментарии:

1. 1) ок, добавьте процесс. Подождите () перед чтением, проверьте. 2) Что вы подразумеваете под чтением std? 3) Есть ли способ выполнить эти команды без cmd.exe ? Я просто поставил это на основе других подобных вопросов на SO и с помощью Google.

2. (1) было скорее проблемой с вызовом ReadToEnd() , чем руководством о том, что делать. В общем, вы должны просто добавить обработчик к OutputDataReceived событию и выполнить richTextBox.Append() его там. Или используйте альтернативный метод, описанный Джоном, который так же действителен, но вам может быть сложнее понять, если вы новичок в асинхронных вызовах.

3. Хорошо, я подписался на process. OutputDataReceived = новый обработчик данных (AppendRichBoxText) и создал метод AppendRichBoxText(отправитель объекта, аргументы DataReceivedEventArgs) { richTextBox1.AppendText(args.Data); }. После я запускаю процесс, а затем вызываю process . BeginOutputReadLine(), я не уверен, что делать дальше.

4. Вам не нужно вызывать BeginOutputReadLine, если вы используете событие. Что касается того, что делать дальше… Idk, это не работает?

5. Похоже, что после того, как я подписываюсь на это событие, а затем предоставляю этот метод eventhandler и затем запускаю процесс, событие OutputDataReceived никогда не срабатывает, так как, когда я ставлю точку останова в методе «AppendRichBoxText», оно никогда не вызывается. Вероятно, я делаю здесь что-то серьезное неправильно.