I am writing a C# application that can launch third party command line executables and redirect their standard output to a RichTextBox.
I am able to redirect the out
If I understand correctly, you are currently seeing progress updates reported to your RichTextBox
control as individual lines, while you would like to emulate the behavior of the console window. I.e. to overwrite the previously output line with the new one, rather than keeping the previous line and adding a new one. With that in mind…
The .NET types, including things like TextReader
and the Process
class's handling of output, consider a newline to be any of \r
, \n
, or \r\n
. So even a process that is simply using a \r
to overwrite the current line would still be interpreted by OutputDataReceived
as starting a new line. Given how consistent this behavior is across .NET, your only real option is to avoid using any built-in line-based I/O mechanisms.
Fortunately, you can get asynchronous reads from the process directly via StandardOutput
, instead of having to handle the line-based OutputDataReceived
event. For example:
async Task ConsumeOutput(TextReader reader, Action<string> callback)
{
char[] buffer = new char[256];
int cch;
while ((cch = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
callback(new string(buffer, 0, cch));
}
}
You might call it like this:
// don't "await", but do keep "task" so that at some later time, you can
// check the result, "await" it, whatever, to determine the outcome.
Task task = ConsumeOutput(process.StandardOutput, s =>
{
richTextBox1.AppendText(s);
});
Of course, without any modification the above will just do what you are already getting. But reading the output this way will not do any interpretation of the line-break characters. Instead, they will be present in the string s
that is passed to the anonymous method handling each read callback. So you can scan that string yourself, looking for lone carriage returns that indicate that you are starting a new line, but should delete the previous line before adding the new text. Be sure to add (or at least skip) all of the text up to the carriage return first, and then delete the last line, before adding the new text.
This code will fix up raw output and make it as it would appear on the terminal.
//Deletes all lines that end with only a CR - this emulates the output as it would be seen cmd
public static string DeleteCRLines( this string Str) {
Str = Str.Replace( "\r\r\n", "\r\n");
//Splits at the CRLF. We will end up with 'lines' that are full of CR - the last CR-seperated-line is the one we keep
var AllLines = new List<string>( Str.Split( new[] {"\r\n"}, StringSplitOptions.None));
for (int i = 0; i < AllLines.Count; i++){
var CRLines = AllLines[i].Split('\r');
AllLines[i] = CRLines[ CRLines.Count() -1];
}
return String.Join( Environment.NewLine, AllLines.ToArray());
}