C#异步读取子进程的stdout [英] C# Read stdout of child process asynchronously

查看:49
本文介绍了C#异步读取子进程的stdout的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用C#,无法理解如何从子进程异步读取 stdout .我想做的是创建一个子进程,该子进程执行一个应用程序,然后在文本框中显示从该进程的stdout接收到的所有内容.我需要立即查看子进程中的每个输出字符,并且不能等待一行完成,因此我认为 Process.OutputDataReceived 事件不适合我的目的.您能告诉我一种明智的方法来完成此操作吗?

I am working with C# and having trouble understanding how to read stdout asynchronously from a child process. What I want to do is create a child process that executes an application and then present whatever is received from that process' stdout in a textbox. I need to see every output character from the child process immediately and can not wait for a line to complete, therefore I don't think that the Process.OutputDataReceived event suits my purpose. Can you tell me a sane way to accomplish this?

我尝试调用 Process.StandardOutput.BaseStream.BeginRead()并将回调函数传递给它,但是在此回调函数中,我从 Process接收到异常.StandardOutput.BaseStream.EndRead().

I have tried calling Process.StandardOutput.BaseStream.BeginRead() and passing a call-back function to it, but in this call-back function I receive an exception from Process.StandardOutput.BaseStream.EndRead().

我的代码如下所示(子进程是一个脚本引擎-缩写为"SE"-用于验证外部设备的功能.脚本按顺序执行,每个脚本都需要SE应用程序的一个实例)

My code looks like this (the child process is a script engine - abbreviated "SE" - for verifying the functionality of an external device. Scripts are executed in sequence and each script requires one instance of the SE application)

private bool startScript()
{
  // Starts the currently indexed script

  if (null != scriptList)
  {

    if (0 == scriptList.Count || scriptListIndexer == scriptList.Count)
    {
      // If indexer equals list count then there are no active scripts in
      // the list or the last active script in the list has just finished
      return false;                              // ## RETURN ##
    }
    if (ScriptExecutionState.RUNNING == scriptList[scriptListIndexer].executionState)
    {
      return false;                              // ## RETURN ##
    }

    if (0 == SE_Path.Length)
    {
      return false;                              // ## RETURN ##
    }

    SE_Process = new Process();
    SE_Process.StartInfo.FileName = SE_Path;
    SE_Process.StartInfo.CreateNoWindow = true;
    SE_Process.StartInfo.UseShellExecute = false;
    SE_Process.StartInfo.RedirectStandardError = true;
    SE_Process.StartInfo.RedirectStandardOutput = true;
    SE_Process.EnableRaisingEvents = true;
    SE_Process.StartInfo.Arguments = scriptList[scriptListIndexer].getParameterString();

    // Subscribe to process exit event
    SE_Process.Exited += new EventHandler(SE_Process_Exited);

    try
    {
      if (SE_Process.Start())
      {
        // Do stuff
      }
      else
      {
        // Do stuff
      }
    }
    catch (Exception exc)
    {
      // Do stuff
    }

    // Assign 'read_SE_StdOut()' as call-back for the event of stdout-data from SE
    SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null);

    return true;                                       // ## RETURN ##

  }
  else
  {
    return false;                                      // ## RETURN ##
  }
}

private void read_SE_StdOut(IAsyncResult result)
{
  try
  {
    int bytesRead = SE_Process.StandardOutput.BaseStream.EndRead(result);  // <-- Throws exceptions

    if (0 != bytesRead)
    {
      // Write the received data in output textbox
      ...
    }

    // Reset the callback
    SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE,     read_SE_StdOut, null);
  }
  catch (Exception exc)
  {
    // Do stuff
  }
}

void SE_Process_Exited(object sender, EventArgs e)
{

  // Keep track of whether or not the next script shall be started
  bool continueSession = false;

  switch (SE_Process.ExitCode)
  {
    case 0: // PASS
    {
      // Do stuff
    }

    ...

  }

  SE_Process.Dispose(); // TODO: Is it necessary to dispose of the process?

  if (continueSession)
  {
    ts_incrementScriptListIndexer();

    if (scriptListIndexer == scriptList.Count)
    {
      // Last script has finished, reset the indexer and re-enable
      // 'scriptListView'
      ...
    }
    else
    {
      if (!startScript())
      {
        // Do stuff
      }
    }
  }
  else
  {
    ts_resetScriptListIndexer();
    threadSafeEnableScriptListView();
  }
}

发生的事情是,一个SE流程完成后,我得到了一个 InvalidOperationException 类型的异常,该异常表示

What happens is that after one SE process finishes I get an exception of type InvalidOperationException that says

StandardOut尚未重定向或该过程尚未开始.

StandardOut has not been redirected or the process hasn't started yet.

从对 SE_Process.StandardOutput.BaseStream.EndRead()的调用中.我不明白为什么,因为我在每个新进程开始之前都设置了 SE_Process.StartInfo.RedirectStandardOutput .在我看来,已处理的进程的标准输出流在处理完毕后会调用我的 read_SE_StdOut()函数,这可能吗?

from the call to SE_Process.StandardOutput.BaseStream.EndRead(). I do not understand why because I have set SE_Process.StartInfo.RedirectStandardOutput before start of every new process. It appears to me as if the stdout stream of an exited process calls my read_SE_StdOut() function after the process has been disposed, is that possible?

感谢您的阅读!

推荐答案

您收到的异常是正常的.一个 BeginRead()调用永远不会成功:在过程终止之后的最后一个调用.如果您知道该过程已完成,那么通常可以避免再次调用 BeginRead(),这样就不会出现异常.但是,您很少知道.赶上例外.或改为使用 BeginOutputReadLine(),它将为您捕获它.

The exception you get is normal. One BeginRead() call can never succeed: the last one, just after the process terminates. You'd normally avoid calling BeginRead() again if you know the process is completed so you don't get the exception. However, you'd rarely know. Just catch the exception. Or use BeginOutputReadLine() instead, it will catch it for you.

我猜您还重定向了 stderr ,并且该工具使用它来输出"X".缓冲和重定向后,无法使 stderr stdout 上的输出保持同步.

I'm guessing that you also redirect stderr and that the tool uses it to output "X". There is no way to keep output on both stderr and stdout synchronized after it is buffered and redirected.

这篇关于C#异步读取子进程的stdout的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆