views:

424

answers:

3

hello,

is it possible to run any Command Line based program or batch file and capturer (re-direct) the out put to a text box LIVE

the CL takes time and it produce text!

something like tracert.exe (it takes time and produce good amount of text).

actually I will work with tracert.exe and I like to capture the output live and show it in a text-box while it is running

EDIT: My problem is to have it LIVE I mean any new line or char the console produce will be sent/pulled to/by the textBox NOT until the program is done!

Simply I want to build is exactly like this http://www.codeproject.com/KB/threads/redir.aspx (check the demo) but in C#

here is my code:

private void button1_Click(object sender, EventArgs e)
{    
     Process pc = new Process();
     pc.StartInfo.FileName = "tracert.exe";
     pc.StartInfo.Arguments = "google.com";
     pc.StartInfo.UseShellExecute = false;
     pc.StartInfo.RedirectStandardOutput = true;
     pc.StartInfo.CreateNoWindow = true;
     pc.Start();

     richTextBox1.Text = pc.StandardOutput.ReadToEnd();

     pc.WaitForExit();    
}

EDIT

with the your help (thanks allot guys) and this link: http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k%28EHINVALIDOPERATION.WINFORMS.ILLEGALCROSSTHREADCALL%29;k%28TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV2.0%22%29;k%28DevLang-CSHARP%29&rd=true

I solve it with this code (do you think it is OK?):

namespace GUIforCL2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        Process _cmd;

        delegate void SetTextCallback(string text);

        private void SetText(string text)
        {
            if (this.richTextBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.richTextBox1.Text += text + Environment.NewLine;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ProcessStartInfo cmdStartInfo = new ProcessStartInfo("tracert.exe");
            cmdStartInfo.Arguments = "google.com";
            cmdStartInfo.CreateNoWindow = true;
            cmdStartInfo.RedirectStandardInput = true;
            cmdStartInfo.RedirectStandardOutput = true;
            cmdStartInfo.RedirectStandardError = true;
            cmdStartInfo.UseShellExecute = false;
            cmdStartInfo.WindowStyle = ProcessWindowStyle.Hidden;

            _cmd = new Process();
            _cmd.StartInfo = cmdStartInfo;

            if (_cmd.Start())
            {
                _cmd.OutputDataReceived += new DataReceivedEventHandler(_cmd_OutputDataReceived);
                _cmd.ErrorDataReceived += new DataReceivedEventHandler(_cmd_ErrorDataReceived);
                _cmd.Exited += new EventHandler(_cmd_Exited);

                _cmd.BeginOutputReadLine();
                _cmd.BeginErrorReadLine();
            }
            else
            {
                _cmd = null;
            }
        }

        void _cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            UpdateConsole(e.Data);
        }

        void _cmd_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            UpdateConsole(e.Data, Brushes.Red);
        }

        void _cmd_Exited(object sender, EventArgs e)
        {
            _cmd.OutputDataReceived -= new DataReceivedEventHandler(_cmd_OutputDataReceived);
            _cmd.Exited -= new EventHandler(_cmd_Exited);
        }

        private void UpdateConsole(string text)
        {
            UpdateConsole(text, null);
        }
        private void UpdateConsole(string text, Brush color)
        {
            WriteLine(text, color);
        }

        private void WriteLine(string text, Brush color)
        {
            if (text != null)
            {    
                SetText(text);
            }
        }
    }
}

cheers

+1  A: 

Yes - create the process using Process.Start(ProcessStartInfo) having set RedirectStandardOutput (and potentially RedirectStandardError) to true and UseShellExecute to false on the ProcessStartInfo describing the process you want to start.

You can then either read directly from Process.StandardOutput and Process.StandardError (in separate threads, so that nothing blocks if the process fills its output buffer) or subscribe to the Process.OutputDataReceived event and call Process.BeginOutputReadLine (and the equivalent for standard error). MSDN has samples of all of this.

Note that in any case, you'll be receiving data on a different thread to the UI thread, so you'll want to use Control.Invoke / Control.BeginInvoke to marshal back to the UI thread before appending the text to the TextBox.

Jon Skeet
I like your code (even if it was not the answer!)
Data-Base
@Data-Base: What code? I only made suggestions... so if this *isn't* the answer, could you explain in what way it fails? Note that this *won't* wait until the process has terminated.
Jon Skeet
sorry, I mean the suggestion, it works, I tested your suggestion,I edited my question to be more clear, Thanks
Data-Base
@Data-Base: No, your question isn't any clearer. The suggestions from both myself and Franci will read the text as it's produced, not waiting for the process to terminate.
Jon Skeet
but is it going to show the text as it's produced? do you have an example? I did it and it did not work! do you want me to post my code?
Data-Base
what I want to build is exactly like this codeproject.com/KB/threads/redir.aspx (check the demo) but in C#
Data-Base
@Data-Base: Yes, I want you to post your code. It should work fine.
Jon Skeet
I just added my code :-)
Data-Base
+3  A: 

When you create the Process instance for tracert, you need to set the ProcessStartInfo.UseShellExecute to false and ProcessStartInfo.RedirectStandardOutput to true. This will allow you to use Process.StandardOutput property to read the output.

You can use Process.BeginOutputReadLine to start asynchronous reading and add an event handler to Process.OutputDataReceived.

Update: Here's an example WPF program that does what you want.

MainWindow.xaml:

<Window x:Class="Console.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" Closed="Window_Closed">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <ScrollViewer Name="outputViewer" SizeChanged="ScrollViewer_SizeChanged" >
            <TextBlock Name="output" />
        </ScrollViewer>
        <TextBox Grid.Row="1" Name="input" KeyDown="input_KeyDown" />
    </Grid>
</Window>

MainWindow.cs:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace Console
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ProcessStartInfo cmdStartInfo = new ProcessStartInfo("cmd.exe");
            cmdStartInfo.CreateNoWindow = true;
            cmdStartInfo.RedirectStandardInput = true;
            cmdStartInfo.RedirectStandardOutput = true;
            cmdStartInfo.RedirectStandardError = true;
            cmdStartInfo.UseShellExecute = false;
            cmdStartInfo.WindowStyle = ProcessWindowStyle.Hidden;

            _cmd = new Process();
            _cmd.StartInfo = cmdStartInfo;

            if (_cmd.Start() == true)
            {
                _cmd.OutputDataReceived += new DataReceivedEventHandler(_cmd_OutputDataReceived);
                _cmd.ErrorDataReceived += new DataReceivedEventHandler(_cmd_ErrorDataReceived);
                _cmd.Exited += new EventHandler(_cmd_Exited);

                _cmd.BeginOutputReadLine();
                _cmd.BeginErrorReadLine();
            }
            else
            {
                _cmd = null;
            }
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            if ((_cmd != null) &&
                (_cmd.HasExited != true))
            {
                _cmd.CancelErrorRead();
                _cmd.CancelOutputRead();
                _cmd.Close();
                _cmd.WaitForExit();
            }
        }

        void _cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            UpdateConsole(e.Data);
        }

        void _cmd_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            UpdateConsole(e.Data, Brushes.Red);
        }

        void _cmd_Exited(object sender, EventArgs e)
        {
            _cmd.OutputDataReceived -= new DataReceivedEventHandler(_cmd_OutputDataReceived);
            _cmd.Exited -= new EventHandler(_cmd_Exited);
        }

        private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            outputViewer.ScrollToBottom();
        }

        private void input_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Enter:
                    _cmd.StandardInput.WriteLine(input.Text);
                    input.Text = "";
                    break;
                case Key.Escape:
                    input.Text = "";
                    break;
            }
        }

        private void UpdateConsole(string text)
        {
            UpdateConsole(text, null);
        }

        private void UpdateConsole(string text, Brush color)
        {
            if (!output.Dispatcher.CheckAccess())
            {
                output.Dispatcher.Invoke(
                        new Action(
                                () =>
                                {
                                    WriteLine(text, color);
                                }
                            )
                    );
            }
            else
            {
                WriteLine(text, color);
            }
        }

        private void WriteLine(string text, Brush color)
        {
            if (text != null)
            {
                Span line = new Span();
                if (color != null)
                {
                    line.Foreground = color;
                }
                foreach (string textLine in text.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
                {
                    line.Inlines.Add(new Run(textLine));
                }
                line.Inlines.Add(new LineBreak());
                output.Inlines.Add(line);
            }
        }

        Process _cmd;
    }
}
Franci Penov
I like your code (even if it was not the answer!)
Data-Base
This _is_ the official way to get live stream of console output from a process in .NET. The alternative is to use the Win32 Console APIs, which the Process class itself uses internally.
Franci Penov
do you have an example? or link to a tutorial?
Data-Base
what I want to build is exactly like this http://www.codeproject.com/KB/threads/redir.aspx (check the demo) but in C#
Data-Base
The CodeProject article does exactly the same think as what Jon Skeet and I outlined, albeit using unmanaged APIs. But the principle is the same - create new process specifying in the startup process info that you want to be able to redirect the standard output and then read the output asynchronously from the output stream (represented as a named pipe in the unmanaged world)
Franci Penov
wow that is awesome :-) Thanks, I have to study this, I never worked with WPF ;-)
Data-Base
A: 

See http://www.codeproject.com/KB/threads/redir.aspx and http://stackoverflow.com/questions/186822/capturing-the-console-output-in-net-c

Daniel Rose
hello, this is good http://www.codeproject.com/KB/threads/redir.aspx but it is in C++ not C# :-(
Data-Base