Lufthansa
Lufthansa

Reputation: 145

How can I redirect process output (console) to richtextbox?

what is wrong why is that the richtextbox doesnt get the stream of Process output? theres no text display in richtextbox..

 private void button1_Click(object sender, EventArgs e)
    {

        Process sortProcess;
        sortProcess = new Process();
        sortProcess.StartInfo.FileName = "sort.exe";
        sortProcess.StartInfo.Arguments = this.comboBox1.SelectedItem.ToString();
        // Set UseShellExecute to false for redirection.
        sortProcess.StartInfo.CreateNoWindow = true;
        sortProcess.StartInfo.UseShellExecute = false;



        // Redirect the standard output of the sort command.  
        // This stream is read asynchronously using an event handler.
        sortProcess.StartInfo.RedirectStandardOutput = true;
        sortOutput = new StringBuilder("");

        // Set our event handler to asynchronously read the sort output.
        sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);

        // Redirect standard input as well.  This stream
        // is used synchronously.
        sortProcess.StartInfo.RedirectStandardInput = true;

        // Start the process.
        sortProcess.Start();



        // Start the asynchronous read of the sort output stream.
        sortProcess.BeginOutputReadLine();
        sortProcess.WaitForExit();


        richTextBox1.AppendText(sortOutput.ToString());
    }

    private static void SortOutputHandler(object sendingProcess,
        DataReceivedEventArgs outLine)
    {
            sortOutput.Append(Environment.NewLine +
                "[" + numOutputLines.ToString() + "] - " + outLine.Data);
        }
    }

so when sort.exe launches, it displays text, i want all those text be displayed also in richtextbox in RealTime (i dont want to wait for the process to exit, and then read all output)

how can i do it? any wrong part of my code? thanks

UPDATE @botz

i added this in my code

 private void SortOutputHandler(object sendingProcess,
        DataReceivedEventArgs outLine)
    {

            sortOutput.Append(Environment.NewLine +
                "[" + numOutputLines.ToString() + "] - " + outLine.Data);
            richTextBox1.AppendText(sortOutput.ToString());


    }

but it throws this exception

Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.

Upvotes: 6

Views: 26825

Answers (5)

Sam Hobbs
Sam Hobbs

Reputation: 2881

As I said in the comment I posted to the question, by definition of what a sort does, it is impossible for there to be any output until all the input has been read. So the sort program is a bad example of getting output in realtime. So the following is for anyone in the future that wants to do something like this for console programs in general. The following uses a BackgroundWorker to get the output asynchronously and put it into a TextBox. A RichTextBox could easily be used instead.

public partial class MainWindow : Window
{
    const string Path = @"C:\Windows\system32\sort.exe";
    BackgroundWorker Processer = new BackgroundWorker();

    public MainWindow()
    {
        InitializeComponent();
        Processer.WorkerReportsProgress = true;
        Processer.WorkerSupportsCancellation = true;
        Processer.ProgressChanged += Processer_ProgressChanged;
        Processer.DoWork += Processer_DoWork;
    }

    private void Processer_DoWork(object sender, DoWorkEventArgs e)
    {
        StreamReader StandardOutput = e.Argument as StreamReader;
        string data = StandardOutput.ReadLine();
        while (data != null)
        {
            Processer.ReportProgress(0, data);
            data = StandardOutput.ReadLine();
        }
    }

    private void Processer_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        string data = e.UserState as string;
        if (data != null)
            DataBox.Text += data + "\r\n";
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        DataBox.Text = string.Empty;
        ProcessStartInfo StartInfo = new ProcessStartInfo(Path);
        StartInfo.RedirectStandardOutput = true;
        StartInfo.RedirectStandardInput = true;
        StartInfo.UseShellExecute = false;
        Process p = null;
        try { p = Process.Start(StartInfo); }
        catch (Exception ex)
        {
            MessageBox.Show($"Error starting {Path}: {ex.Message}");
            return;
        }
        // Get the output
        Processer.RunWorkerAsync(p.StandardOutput);
        // Put the input
        p.StandardInput.WriteLine("John");
        p.StandardInput.WriteLine("Alice");
        p.StandardInput.WriteLine("Zoe");
        p.StandardInput.WriteLine("Bob");
        p.StandardInput.WriteLine("Mary");
        // Tell the program that is the last of the data
        p.StandardInput.Close();
    }
}

For the sort program it is not necessary to call ReportProgress until after all the data has been read but this is a more generalized sample.

Upvotes: 0

Harshal Doshi  Jain
Harshal Doshi Jain

Reputation: 2597

Complete application and source code available from this external link of codeproject :

http://www.codeproject.com/Articles/335909/Embedding-a-Console-in-a-C-Application

This is tutorial of implementation of https://github.com/dwmkerr/consolecontrol.

Upvotes: 0

Botz3000
Botz3000

Reputation: 39610

WaitForExit() blocks your UI Thread, so you don't see the new output. Either wait for the process in a separate thread or replace WaitForExit() with something like this:

while (!sortProcess.HasExited) {
     Application.DoEvents(); // This keeps your form responsive by processing events
}

In your SortOutputHandler, you can now directly append output to your textbox. But you should remember to check if you need to invoke it on the UI Thread.

You can check if it's on the UI thread this way in your handler:

    if (richTextBox1.InvokeRequired) { richTextBox1.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine }); }
    else {
        sortOutput.Append(Environment.NewLine + "[" + numOutputLines.ToString() + "] - " + outLine.Data);
        richTextBox1.AppendText(sortOutput.ToString());
    }

Upvotes: 7

Sorin Comanescu
Sorin Comanescu

Reputation: 4867

This is working for me:

private void button1_Click(object sender, EventArgs e)
    {
        using (Process sortProcess = new Process())
        {
            sortProcess.StartInfo.FileName = @"F:\echo_hello.bat";
            sortProcess.StartInfo.CreateNoWindow = true;
            sortProcess.StartInfo.UseShellExecute = false;
            sortProcess.StartInfo.RedirectStandardOutput = true;

            // Set event handler
            sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);

            // Start the process.
            sortProcess.Start();

            // Start the asynchronous read
            sortProcess.BeginOutputReadLine();

            sortProcess.WaitForExit();
        }
    }

    void SortOutputHandler(object sender, DataReceivedEventArgs e)
    {
        Trace.WriteLine(e.Data);
        this.BeginInvoke(new MethodInvoker(() =>
        {
            richTextBox1.AppendText(e.Data ?? string.Empty);
        }));
    }

The example you started with was a console application, which doesn't care much about multithreaded access. For Windows Forms when you update a control this has to be done from the main UI thread, which is why BeginInvoke is needed. If you want to check rapidly if a handler like SortOutputHandler is working properly you can use System.Diagnostics.Trace.Write*, which doesn't need BeginInvoke.

EDIT: echo_hello.bat simply echoes the "hello" string:

@echo off
echo hello

Upvotes: 5

Rob
Rob

Reputation: 3066

If you are going to update the ui from another thread, you need to make sure you are on the main ui thread. In the method check for InvokeRequired. See InvokeRequired

Upvotes: 0

Related Questions