简体   繁体   中英

C# Wait for all dates from redirecting

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

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"!

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()); } while(true) { Console.WriteLine(random()); } and the UI was slower but still responsive.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM