【问题标题】:Get .NET Process object to flush input stream continously?获取 .NET Process 对象以连续刷新输入流?
【发布时间】:2011-09-29 13:21:08
【问题描述】:

我正在尝试更改与 Mercurial 命令行客户端对话的类库,而 1.9 客户端中的新功能是能够启动服务器并通过标准输入/输出管道与其对话。这确实很有希望,因为使用 Mercurial 命令行客户端的主要问题之一是,由于它是用 Python 编写的,因此启动客户端会产生一些开销,即使只是询问它的版本也是如此。

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

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

换句话说,由于进程没有退出,所以直到:

  1. 我要求进程退出
  2. 我发出另一个命令

有没有办法让我

  1. 请求缓冲区刷新,这意味着我得到缓冲区中的任何内容,即使它不足以使缓冲区自行刷新?
  2. 如果不是,我可以将缓冲区大小设置为 1 个字符吗?
  3. 我还能做什么?

如果需要的话,我可以处理 P/Invoke。

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

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

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

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

但是,下面的程序看到的是这样的:

  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);
    

    }

【问题讨论】:

  • 我改程序去掉了对Mercurial的依赖,现在用常规的CMD.EXE程序来说明。
  • 您好,我也遇到了同样的问题,您解决了吗?如果是这样,怎么办? :)

标签: .net process buffer redirectstandardoutput


【解决方案1】:

我不认为有任何方法可以强制刷新,但是我有几个替代方案可能会对您有所帮助:

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

HTH,

巴特

【讨论】:

  • 当然,既然你提到它,它可能是一个完全自我强加的缓冲区,一个完整的行 :P 由于 Mercurial 命令客户端的输出无论如何都会在中间有一些时髦的字符,它会说一些事情像“ocharacters”,我需要去掉那些字节,我只是假设缓冲在流中,但当然在 ReadLine 中也有一个,也许如果我刚刚写了我的代码如果我不知道其中的区别,请先阅读该输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-27
  • 2015-09-27
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多