0

I am redirecting output from my process (proc) to richtextbox (richTextBoxOutput). Before redirect, it write to the richtextbox sentence "BEFORE OUTPUT DATA". Then it should write all data from process and after write sentence "AFTER OUTPUT DATA". But, this last sentence is never in the end. It is always somewhere in the middle of richtextbox between redirect dates. Can you help me with a solution?

richTextBoxOutput.AppendText("BEFORE OUTPUT DATA");
Process proc = new Process();
proc.StartInfo.FileName = command;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.OutputDataReceived += new DataReceivedEventHandler
(
            (s, e) =>
            {
                if (richTextBoxOutput.InvokeRequired)
                {
                    richTextBoxOutput.Invoke(new Action(() => richTextBoxOutput.AppendText(e.Data + "\n")));
                }
                else
                    richTextBoxOutput.AppendText(e.Data + "\n");
            }
);
proc.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { richTextBoxOutput.AppendText(e.Data + "\n"); });
proc.Start();
proc.BeginOutputReadLine();
while (!proc.HasExited)
{
        Application.DoEvents(); //Instead of proc.WaitForExit()             
}
richTextBoxOutput.AppendText("AFTER OUTPUT DATA");
Guestík
  • 113
  • 1
  • 9
  • check this [C# : Redirect console application output : How to flush the output?](http://stackoverflow.com/questions/1033648/c-sharp-redirect-console-application-output-how-to-flush-the-output) – bansi Dec 14 '15 at 11:41
  • also try `proc.CancelOutputRead();` after the proc has exited. This will complete all the in-progress read operations. – bansi Dec 14 '15 at 11:51
  • When I use proc.CancelOutputRead(), it works, but sometimes it's faster than output, which is not displayed all. – Guestík Dec 14 '15 at 12:30
  • How can I check, if OutputDataReceived ends with output? – Guestík Dec 14 '15 at 12:44
  • I would suggest not using `Application.DoEvents();` it can get you into a lot of trouble. Try running the entire code in another thread than the main UI thread. then you can use `proc.WaitForExit` or you can even use synchronous read. – bansi Dec 15 '15 at 04:55
  • just found this [How to read to end process output asynchronously in C#?](http://stackoverflow.com/questions/9533070/how-to-read-to-end-process-output-asynchronously-in-c), you may be interested it uses both synchronous and asynchronous methods to do the trick! – bansi Dec 15 '15 at 04:58

2 Answers2

1

May I suggest a Async approach to the problem?
Yes you will have to handle cross-thread calls because you are writing to a windows form (textbox like) component from another thread, but the good part is that you can use the proc.WaitForExit without worrying about freezing the interface (without that nasty loop that is highly ineffective because it will have your app burn-out the CPU) AND have a ContinueWith clause to append your "AFTER OUTPUT DATA"!

Leonardo
  • 10,737
  • 10
  • 62
  • 155
  • But I want to keep realtime redirect. – Guestík Dec 14 '15 at 13:45
  • @Guestík The output from process can be faster than you can add text to rightextbox. So in some cases you can not do it realtime at all. – TcKs Dec 14 '15 at 14:07
  • @Guestík TcKs is right here... realtime anything is a fallacy! and in this specific case the delay would be VERY small... i would say that even you would not notice it... we're talking about a single extra thread... – Leonardo Dec 14 '15 at 15:56
  • What about cp command, that copies files and in realtime displaying that are already copied (-v parameter). I don't want frozen application along this time. btw Sorry about my english. – Guestík Dec 14 '15 at 16:05
  • @Guestík no, it does not update in real time... don't trust me? remember that your computer is prob running about 800 threads (mine is running 1600 across 120 processes), and you have something between 4-12 cores only... as a proof of concept, you can override the Control.WndProc (https://msdn.microsoft.com/en-us/library/system.windows.forms.control.wndproc(v=vs.110).aspx) method of a base Form and see that thousands of messages pass between your "realtime" update... – Leonardo Dec 14 '15 at 16:22
  • Maybe everyone talking about something else. My english is at low level, so maybe its reason. But can you please show me a bit of a code? I really don't know how could I write it now. – Guestík Dec 14 '15 at 17:29
1

Minimal working sample:

    private void btnRun_Click(object sender, EventArgs e) {
        Task.Factory.StartNew(this.StdOutWorker);
    }

    private void StdOutWorker() {
        this.AppendLine("BEFORE OUTPUT DATA");

        // "CmdRandomGenerator.exe" will print random numbers to standard output in infinity loop
        ProcessStartInfo pi = new ProcessStartInfo("CmdRandomGenerator.exe") {
            RedirectStandardOutput = true,
            UseShellExecute = false
        };
        var proc = new Process{
            StartInfo = pi,
            EnableRaisingEvents = true
        };
        proc.Start();

        while (!proc.HasExited) {
            var line = proc.StandardOutput.ReadLine();
            this.AppendLine(line);
        }

        this.AppendLine("AFTER OUTPUT DATA");
    }

    private void AppendLine(string line) {
        Action act = () => {
            this.rtbOutput.AppendText(line + Environment.NewLine);
        };

        // UI objects must be accessed in UI thread
        this.BeginInvoke(act);
    }

The idea is, the reading from process is in background thread, and the values are passed to UI thread without waiting for result (fire and forget).

Theoretically, the values from observed process may come faster than the UI can handle that. In that case you must throw away some values (sampling). However I tried that with while(true) { Console.WriteLine(random()); } and the UI was slower but still responsive.

TcKs
  • 25,849
  • 11
  • 66
  • 104