【问题标题】:Realtime Console Output Redirection using Process使用进程的实时控制台输出重定向
【发布时间】:2020-02-19 15:19:29
【问题描述】:

我正在使用 VBOXMANAGE 来“导出”客户机。 VBOXManage 是一个控制台应用程序,可以从主机控制来宾机器的行为。由于导出命令是一个漫长的过程,它返回的过程更新如下:

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

我正在编写一个 C# 应用程序,它将使用 Process 调用 VBOXManage。这是我的代码:

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%”只会在实际过程完成后显示。

有没有办法实时捕获控制台输出?

谢谢!

【问题讨论】:

  • 注意函数名:BeginOutput ReadLine
  • 是的,感谢 nobugz 的精彩见解。 ;)
  • 几个With 语句将使代码更容易在眼睛(和剪贴板)上显示...With VBOXProc ... With .StartInfo ... End With ... End With

标签: c# process console-redirect


【解决方案1】:

这对我有用:

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()是我使用的方法。

【讨论】:

  • 只有在输出流终止时才有效。但在我的示例中,输出流在一行上,并且仅在处理完成时终止。
【解决方案2】:

您可以使用所有标准 Stream 方法直接从 StandardOutput/Error 读取进程,只需确保将 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());

以上内容会将子进程的输出回显到您的应用程序标准输出。

您可以使用 p.StandardOutput.Read(char[], int, int) 读取特定大小的块,也可以使用 p.StadardOutput.BaseStream.BeginRead(...) 进行异步读取。

所有相同的方法都可用于 StandardError。

在循环中休眠可以为其他任务释放处理器,并允许一些数据在缓冲区中累积。如果睡眠时间过长,缓冲区溢出,执行进程的一些输出将会丢失。如果睡眠时间太短,则会花费大量 CPU 周期来读取和清空缓冲区。

【讨论】:

  • 嗨,上面的代码有点不可靠...我收到截断的文本。
  • 是的,根据写入标准输出的时间和进程终止,循环终止后缓冲区中可能会留下一些数据。循环后的另一次读取将拾取任何丢失的数据。我主要想指出所有流方法都可用。在生产代码中,我可能会在循环中休眠或以其他方式释放处理器,并且只会“偶尔”读取。
  • ReadToEnd() 将在进程运行时阻塞。
【解决方案3】:

也尝试重定向标准输入并将 AutoFlush 应用于 StandardInput。使用 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】:

    抱歉,我是巴西人,使用谷歌翻译来写这篇文章。

    巧合的是,我也在做一个与 Virtualbox 的 VBoxManage 配合使用的程序。就我而言,除其他外,我还想转换一个虚拟磁盘。它也延迟和进度百分比也

    我设法做到这一点,方法是创建一个运行程序的意志进程,并使用一个用户类“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() 使用 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 换行时调用。有时文本为空,然后像我在使用为控件获取的文本之前所做的那样放置一个检查结构。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-04
      相关资源
      最近更新 更多