Перенаправление выходных данных в режиме реального времени с использованием процесса

Я использую VBOXMANAGE для "экспорта" гостевой машины. VBOXManage - это консольное приложение, которое может управлять поведением гостевой машины с хоста. Поскольку команда export является длинным процессом, она возвращает обновления процесса следующим образом:

0%... 10%... 20%... 30%... 100%

Я пишу приложение С#, которое будет вызывать VBOXManage с помощью Process. Здесь мой код:

Process VBOXProc = new Process();

VBOXProc.StartInfo.FileName = VBOXMANAGE;
VBOXProc.StartInfo.Arguments = Arguments;
VBOXProc.StartInfo.UseShellExecute = false;
VBOXProc.StartInfo.CreateNoWindow = true;
VBOXProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
VBOXProc.StartInfo.RedirectStandardError = true;
VBOXProc.StartInfo.RedirectStandardOutput = true;

VBOXProc.OutputDataReceived += new DataReceivedEventHandler(VBOXProc_OutputDataReceived);
VBOXProc.ErrorDataReceived += new DataReceivedEventHandler(VBOXProc_ErrorDataReceived);

VBOXProc.EnableRaisingEvents = true;

VBOXProc.Start();
VBOXProc.BeginOutputReadLine();
VBOXProc.BeginErrorReadLine();

VBOXProc.WaitForExit();

Это хорошо, за исключением того, что вывод читается на LINE. Это означает, что процесс обновляет "0%... 10%... 20%... 30%... 100%" будет отображаться только после того, как будет выполнен фактический процесс.

Есть ли способ захвата вывода консоли в реальном времени?

Спасибо!

Ответ 1

Вы можете читать непосредственно из StanadardOutput/Error для процесса, используя все стандартные методы Stream, просто установите для параметра StartInfo.Redirectxxx значение true.

var p = new Process()
p.StartInfo.UseShellExecute = false;  //not sure if this is absolutely required
p.StartInfo.RedirectStandardOuput = true;
....

do
{
  Thread.Sleep(nnn);
  Console.Out.Write(p.StandardOutput.ReadToEnd());
}
while (!p.HasExited);
//catch any leftovers in redirected stdout
Console.Out.Write(p.StandardOutput.ReadToEnd());

Вышеизложенное будет выводить вывод дочернего процесса в ваши приложения Standard Out.

Вы можете читать блоки определенного размера, используя p.StandardOutput.Read(char [], int, int) или асинхронные чтения с использованием p.StadardOutput.BaseStream.BeginRead(...).

Все одинаковые методы доступны для StandardError.

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

Ответ 2

Это сработало для меня

        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.ErrorDialog = false;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.UseShellExecute = false;

        process.ErrorDataReceived += (sendingProcess, errorLine) => error.AppendLine(errorLine.Data);
        process.OutputDataReceived += (sendingProcess, dataLine) => SetMessage(dataLine.Data);

        process.Start();
        process.BeginErrorReadLine();
        process.BeginOutputReadLine();

        process.WaitForExit();

error.AppendLine() и SetMessage() - мои методы.

Ответ 3

Попробуйте также перенаправить стандартный ввод и примените AutoFlush к стандарту. Следующий поток чтения с использованием StreamReader.

Process proces;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "test.exe";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;

proces = Process.Start(psi);
proces.StandardInput.AutoFlush = true;

Ответ 4

Извините, я ошибся, я бразилец и использую Google Translate, чтобы написать этот текст.

Кстати, я также делаю программу, которая работает с VBoxManage Virtualbox. В моем случае я хотел, среди прочего, преобразовать виртуальный диск. Также он задерживает и процент с прогрессом также

Мне удалось это сделать, создав процесс воли для запуска программы и используя пользовательские классы Dean North, другие question, которые аналогично этому. Важно использовать поток для запуска VBoxManage, иначе не получится обработать полученный текст или просмотреть ход.

O texto é muito grande pra eu adicionar quatro espaços antes de cada linha e repassar.

Классы заменяют системный класс Process. Не нужно вносить какие-либо изменения в свой код, просто добавьте arquivo.cs с текстом, переданным пользователем Dean North вместо Process p = new Process() use FixedProcess p = new FixedProcess ()

После этого это был мой код:

private void BotaoParaTestes_Click(object sender, EventArgs e)
{
    string linha = @"clonehd " +
        "\"Z:\\Máquinas Virtuais\\Teste.vdi\" " +
        "\"C:\\Temp\\teste.vdi\" " +
        "--format VDI --variant Standard";

    Thread tarefa = new Thread(Executar);
    tarefa.Start(linha);
}
private void Executar(object Linha)
{
    FixedProcess fp = new FixedProcess ();
    fp.StartInfo.FileName = ItensEstaticos.VBox;
    fp.StartInfo.Arguments = Linha.ToString();
    fp.StartInfo.CreateNoWindow = true;
    fp.StartInfo.ErrorDialog = false;
    fp.StartInfo.RedirectStandardError = true;
    fp.StartInfo.RedirectStandardOutput = true;
    fp.StartInfo.UseShellExecute = false;
    fp.ErrorDataReceived += (sendingProcess, errorLine) => Escrita(errorLine.Data);
    fp.OutputDataReceived += (sendingProcess, dataLine) => Escrita(dataLine.Data);
    fp.Start();
    fp.BeginErrorReadLine();
    fp.BeginOutputReadLine();

    fp.WaitForExit();
}
private void Escrita(string Texto)
{
    if (!string.IsNullOrEmpty(Texto))
    {
        BeginInvoke(new MethodInvoker(delegate
        {
            this.Texto.Text += Texto;
        }));
    }
}

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