views:

165

answers:

2

I was trying to put together an interactive Console interceptor/wrapper in C# over the weekend, by re-mixing few code samples I've found in SO and other sites.

With what I've as of now, I'm unable to read back from the console reliably. Any quick pointers?

public class ConsoleInterceptor
{
    Process _interProc;

    public event Action<string> OutputReceivedEvent;

    public ConsoleInterceptor()
    {
        _interProc = new Process();
        _interProc.StartInfo = new ProcessStartInfo("cmd");
        InitializeInterpreter();
    }

    public ConsoleInterceptor(string command)
    {
        _interProc = new Process();
        _interProc.StartInfo = new ProcessStartInfo(command);
        InitializeInterpreter();
    }

    public Process InterProc
    {
        get
        {
            return _interProc;
        }
    }

    private void InitializeInterpreter()
    {
        InterProc.StartInfo.RedirectStandardInput = true;
        InterProc.StartInfo.RedirectStandardOutput = true;
        InterProc.StartInfo.RedirectStandardError = true;
        InterProc.StartInfo.CreateNoWindow = true;
        InterProc.StartInfo.UseShellExecute = false;
        InterProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        bool started = InterProc.Start();

        Redirect(InterProc.StandardOutput);
        Redirect(InterProc.StandardError);

    }

    private void Redirect(StreamReader input)
    {
        new Thread((a) =>
        {
            var buffer = new char[1];
            while (true)
            {
                if (input.Read(buffer, 0, 1) > 0)
                    OutputReceived(new string(buffer));
            };
        }).Start();
    }

    private void OutputReceived(string text)
    {
        if (OutputReceivedEvent != null)
            OutputReceivedEvent(text);
    }


    public void Input(string input)
    {
        InterProc.StandardInput.WriteLine(input);
        InterProc.StandardInput.Flush();
    }
}

What I'm trying to accomplish? Here is a minial use case. Assume that I have two textboxes.

//Create my interceptor
 ConsoleInterceptor interc = new ConsoleInterceptor("cmd");
//Show the output in a textbox
     interc.OutputReceivedEvent += (data) =>
                {
                    this.Invoke(new Action<string>((s)=> this.textBoxOut.Text += s) ,data);
                };



 //Capture user input and pass that to the above interceptor
  private void textInput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                interc.Input(textInput.Text);
            }
        }
A: 

Instead of using another thread looping over the output stream, you can attach a handler to the Process.OutputDataReceived Event, which, after you've called BeginOutputReadLine, is raised when the process writes a line to the redirected StandardOutput stream (which you've already done).

There's a full example in the link which should hopefully help.

Sam
Already tried with BeginOutputReadLine + OutputDataReceived - If you've tried that out, you'll find that u've an issue there as well.
amazedsaint
A: 

In order to fully be compatible with any console process, you need three separate threads: one writing to stdin, one reading from stdout, and one reading from stderr. These are in addition to your main thread. Your example code only has one of the three threads required (stdout).

Stephen Cleary
In the Redirect, I'm creating two threads if you've noticed - one for reading stderr, one for stdout.
amazedsaint