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:
- I ask the process to exit
- I issue another command
Is there a way for me to
- 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?
- If not, can I set the buffer-size to 1 character?
- 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:
- Produce directory listing
- Ask for another command by prompting "C:>\"
However, this is what the program below sees:
- Produce directory listing
- (wait 10 seconds)
- Close the input stream
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(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);
}