获取.NET Process对象以连续刷新输入流?

时间:2011-07-06 11:34:04

标签: .net process buffer redirectstandardoutput

我正在尝试更改与Mercurial命令行客户端对话的类库,而1.9客户端中的新功能是能够启动服务器并通过标准输入/输出管道与之通信。这是非常有希望的,因为使用Mercurial命令行客户端的一个主要问题是,因为它是用Python编写的,所以有一些开销会激活客户端,即使只是询问它的版本。

然而,有一个问题。到目前为止,我通过读取标准输出/错误检索了命令行客户端的输出,当进程退出时,流刷新,我可以读取流的结尾。

但是,在这种新模式下,进程会将大量文本转储到标准错误/输出,然后等待下一个命令。非常适合减少开销,但由于.NET缓冲了这些流,因此很糟糕。

换句话说,由于进程没有退出,我直到输出该输出的最后一部分:

  1. 我要求退出流程
  2. 我发出另一个命令
  3. 我有办法吗

    1. 请求缓冲区刷新,这意味着我得到缓冲区中的任何内容,即使它不足以使缓冲区自行刷新?
    2. 如果没有,我可以将缓冲区大小设置为1个字符吗?
    3. 我能做的其他事情吗?
    4. 如果需要,我可以处理P / Invoke。

      这是一个LINQPad概念验证程序,可以说明:

      它的作用是启动命令提示符并将其提供给DIR命令,但是请注意,直到循环内已经过了10秒才会输出“C:>”提示在生成目录列表后立即输出命令提示符。

      换句话说,这就是命令提示符的作用:

      1. 制作目录列表
      2. 通过提示“C:> \”
      3. 询问另一个命令

        但是,这就是下面的程序:

        1. 制作目录列表
        2. (等待10秒)
        3. 关闭输入流
        4. 查看提示

          void Main() {     string clientExecutablePath =“cmd.exe”;

          var psi = new ProcessStartInfo();
          psi.FileName = clientExecutablePath;
          psi.RedirectStandardError = true;
          psi.RedirectStandardInput = true;
          psi.RedirectStandardOutput = true;
          psi.CreateNoWindow = true;
          psi.WorkingDirectory = @"C:\";
          psi.WindowStyle = ProcessWindowStyle.Hidden;
          psi.UseShellExecute = false;
          psi.ErrorDialog = false;
          psi.StandardErrorEncoding = Encoding.GetEncoding("Windows-1252");
          psi.StandardOutputEncoding = Encoding.GetEncoding("Windows-1252");
          
          var p = Process.Start(psi);
          
          var input = p.StandardInput;
          var output = p.StandardOutput;
          var error = p.StandardError;
          
          Action<StreamReader, string> reader = delegate(StreamReader streamReader, string prefix)
          {
              string line;
              while ((line = streamReader.ReadLine()) != null)
              {
                  Debug.WriteLine(prefix + line);
              }
          };
          
          IAsyncResult outputReader = reader.BeginInvoke(output, "o: ", null, null);
          IAsyncResult errorReader = reader.BeginInvoke(error, "e: ", null, null);
          
          input.Write("dir\n");
          input.Flush();
          
          while (!p.HasExited)
          {
              Thread.Sleep(10000);
              input.Close();
          }
          
          reader.EndInvoke(outputReader);
          reader.EndInvoke(errorReader);
          

          }

1 个答案:

答案 0 :(得分:1)

我认为没有办法强制冲洗,但我有几个可能对你有帮助的选择:

  1. 我注意到你正在使用ReadLine()来读取流。在写入完整的输出行(包括CRLF或至少LF)之前,这不会返回。因此,您可能需要考虑使用Read(),它只能读取单个字符。如果服务器进程没有在最后一行写入CRLF直到它退出,你可能会发现这可以获得你正在寻找的最后一块输出。
  2. 您可以通过向Process上的OutputDataReceived和ErrorDataReceived事件添加处理程序来异步读取流,然后使用BeginOutputReadLine和BeginErrorReadLine开始接收数据。我不知道调用事件的频率如何:每当有任何数据可用时,无论何时收到完整的数据行,或每个字符,都会定期调用事件。
  3. HTH,

    巴特