开发者

Get .NET Process object to flush input stream continously?

I am trying to change my class library that talks to the Mercurial command line client, and new in the 1.9 client is the ability to spin up a server and talk to it over the standard input/output pipes. This is really promising since one of the major headaches of using the Mercurial command line client is that since it is written in Python, there's a bit of overhead spinning up the client, even for just asking it for its version.

However, there is one problem. Up until now I have retrieved the output from the command line client by reading the standard output/error, and when the process exits, the streams flush and I can read the end of the stream.

However, in this new mode, the process will dump a lot of text to the standard error/output, and then sit around waiting for the next command. Great for reducing overhead, but lousy since .NET buffers those streams.

In other words, since the process does not exit, I do not get the last portion of that output until:

  1. I ask the process to exit
  2. I issue another command

Is there a way for me to

  1. Ask the buffer to flush, meaning that I get whatever is in the buffer, even if it is not enough to make the buffer flush by itself?
  2. If not, can I set the buffer-size to 1 character?
  3. Anything else I can do?

I can deal with P/Invoke if that is what it takes.

Here's a LINQPad proof-of-concept program that would illustrate:

What it does is spin up a command prompt and feed it the DIR command, however notice that not until those 10 seconds have elapsed inside the loop does the program output the "C:>" prompt that the command prompt outputted right after producing the directory listing.

In other words, this is what the command prompt does:

  1. Produce directory listing
  2. Ask for another command by prompting "C:>\"

However, this is what the program below sees:

  1. Produce directory listing
  2. (wait 10 seconds)
  3. Close the input stream
  4. See the prompt

    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(Stre开发者_StackOverflow社区amReader 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);
    

    }


I don't think there's any way to force a flush, however I have a couple of alternatives which might help you:

  1. I notice you're using ReadLine() to read the stream. This won't return until a complete line of output (including CRLF, or at least LF) has been written. You might therefore want to consider using Read() instead, which will read only a single character. You may find this gets you the last chunk of output you're looking for if the server process isn't writing a CRLF on the last line until it exits.
  2. You could read the streams asynchronously by adding handlers to the OutputDataReceived and ErrorDataReceived events on the Process, then use BeginOutputReadLine and BeginErrorReadLine to start receiving data. I don't know how often the events are invoked though: at regular intervals if any data is available, whenever a complete line of data is received, or for every character.

HTH,

Bart

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜