Как мне получить вывод команды, который будет отображаться в элементе управления формы в режиме реального времени?C#

Место общения программистов C#
Ответить
Anonymous
 Как мне получить вывод команды, который будет отображаться в элементе управления формы в режиме реального времени?

Сообщение Anonymous »

Из различных источников в Интернете я собрал следующий код для выполнения команды через CMD.exe и захвата вывода из STDOUT и STDERR.

Код: Выделить всё

public static class Exec
{
public delegate void OutputHandler(String line);

// 
/// Run a command in a subprocess
/// 
/// 
Directory from which to execute the command
/// Command to execute
/// Arguments for command
/// Command output handler (null if none)
/// True if no windows is to be shown
/// Exit code from executed command
public static int Run(String path, String cmd, String args,
OutputHandler hndlr = null, Boolean noshow = true)
{
// Assume an error
int ret = 1;
// Create a process
using (var p = new Process())
{
// Run command using CMD.EXE
// (this way we can pipe STDERR to STDOUT so they can get handled together)
p.StartInfo.FileName = "cmd.exe";
// Set working directory (if supplied)
if (!String.IsNullOrWhiteSpace(path)) p.StartInfo.WorkingDirectory = path;
// Indicate command and arguments
p.StartInfo.Arguments = "/c \"" + cmd + " " + args + "\" 2>&1";
// Handle noshow argument
p.StartInfo.CreateNoWindow = noshow;
p.StartInfo.UseShellExecute = false;
// See if handler provided
if (hndlr != null)
{
// Redirect STDOUT and STDERR
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
// Use custom event handler to capture output
using (var outputWaitHandle = new AutoResetEvent(false))
{
p.OutputDataReceived += (sender, e) =>
{
// See if there is any data
if (e.Data == null)
{
// Signal output processing complete
outputWaitHandle.Set();
}
else
{
// Pass string to string handler
hndlr(e.Data);
}
};
// Start process
p.Start();
// Begin async read
p.BeginOutputReadLine();
// Wait for process to terminate
p.WaitForExit();
// Wait on output processing complete signal
outputWaitHandle.WaitOne();
}
}
else
{
// Start process
p.Start();
// Wait for process to terminate
p.WaitForExit();
}
// Get exit code
ret = p.ExitCode;
}
// Return result
return ret;
}

// 
/// Run a command in a subprocess and return output in a variable
/// 
/// Directory from which to execute the command
/// Command to execute
/// Arguments for command
/// Variable to contain the output
/// Exit code from executed command
public static GetOutputReturn GetOutput(String path, String cmd, String args)
{
GetOutputReturn ret = new GetOutputReturn();
ret.ReturnCode = Run(path, cmd, args,  (line) =>
{
ret.Output.AppendLine(line);
});
return ret;
}
}

public class GetOutputReturn
{
public StringBuilder Output = new StringBuilder();
public int ReturnCode = 1;
}
Я могу использовать это в консольном приложении тремя разными способами:

Код: Выделить всё

static void Main(string[] args)
{
int ret;
Console.WriteLine("Executing dir with no capture and no window");
ret = Exec.Run(@"C:\", "dir", "");
Console.WriteLine("Execute returned " + ret);
Console.WriteLine("Press enter to continue ...");
Console.ReadLine();
Console.WriteLine("Executing dir with no capture and window");
ret = Exec.Run(@"C:\", "dir", "", null, false);
Console.WriteLine("Execute returned " + ret);
Console.WriteLine("Press enter to continue ...");
Console.ReadLine();
Console.WriteLine("Executing dir with capture and no window");
var results = Exec.GetOutput(@"C:\", "dir", "");
Console.WriteLine(results.Output.ToString());
Console.WriteLine("Execute returned " + results.ReturnCode);
Console.ReadLine();
Console.WriteLine("Executing dir with real-time capture and no window");
ret = Exec.Run(@"C:\", "dir", "", ShowString);
Console.WriteLine("Execute returned " + ret);
}

public delegate void StringData(String str);

static void ShowString(String str)
{
Console.WriteLine(str);
}

public delegate void StringData(String str);

static void ShowString(String str)
{
Console.WriteLine(str);
}
Первый запуск не собирает никаких выходных данных, а просто показывает код выхода.

Второй запуск не собирает никаких выходных данных, но показывает окно.

В результате выходные данные появляются в окне консоли в режиме реального времени.

Третий запуск использует GetOutput для сбора выходных данных.

В результате этого выходные данные не появляются до завершения выполнения.

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

Внешне это похоже на второй запуск, но он сильно отличается.

Для каждой полученной строки вывода вызывается ShowString.

Show string просто отображает строку.

Однако он может делать с данными все, что ему нужно.

Я пытаюсь адаптировать последний запуск так, чтобы можно было обновлять текстовое поле с выводом команды в режиме реального времени. Проблема, с которой я столкнулся, заключается в том, как получить это в правильном контексте (из-за отсутствия лучшего термина). Поскольку OutputHandler вызывается асинхронно, для синхронизации с потоком пользовательского интерфейса он должен использовать механизм InvokeRequired/BeginInvoke/EndInvoke. У меня возникла небольшая проблема с тем, как это сделать с параметрами. В моем коде textBox может быть одним из нескольких в элементе управления вкладками, поскольку может иметь место несколько фоновых «Выполнить».

Пока у меня есть это:

Код: Выделить всё

private void btnExecute_Click(object sender, EventArgs e)
{
// Get currently selected tab page
var page = tcExecControl.SelectedTab;
// Get text box (always 3rd control on the page)
var txt = (TextBox)page.Controls[2];
// Create string handler
var prc = new Exec.OutputHandler((String line) =>
{
if (txt.InvokeRequired)
txt.Invoke(new MethodInvoker(() =>
{ txt.Text += line; }));
else txt.Text += line;
});
// Command and arguments are always 1st and 2nd controls on the page
var result = Exec.Run(@"C:\", page.Controls[0].Text, page.Controls[1], prc);
}
Но, похоже, это не работает. Я не вижу никакого вывода в txtBox.

На самом деле программа в основном зависает в обработчике.

Если я изменю код на использование GetOutput, а затем запишу полученный вывод в текстовое поле, все будет работать. Итак, я знаю, что у меня настроена команда правильно. Используя отладчик, я могу установить точку останова на "if (

Код: Выделить всё

txt.InvokeRequired
)", и я вижу, что первая строка вывода отображается правильно. На этом этапе код выбирает истинный путь оператора if, но если я установлю точку останова на

Код: Выделить всё

txt.Text += line;
[/b] линия туда никогда не попадает.

Может ли кто-нибудь мне помочь? Я уверен, что что-то упускаю.

Подробнее здесь: https://stackoverflow.com/questions/516 ... -real-time
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»