Sending input/getting output from a console application (C#/WinForms)

17,552

Solution 1

If you want something interactive, I got this code to work (yours modified, details on modifications below)

    private void InitializeInterpreter()
    {
        InterProc.StartInfo.UseShellExecute = false;
        InterProc.StartInfo.FileName = "Echoer.exe";
        InterProc.StartInfo.RedirectStandardInput = true;
        InterProc.StartInfo.RedirectStandardOutput = true;
        InterProc.StartInfo.RedirectStandardError = true;
        InterProc.StartInfo.CreateNoWindow = true;
        InterProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        InterProc.OutputDataReceived += new DataReceivedEventHandler(InterProcOutputHandler);

        bool started = InterProc.Start();

        InterProc.BeginOutputReadLine();

    }

    private void AppendTextInBox(TextBox box, string text)
    {
        if (this.InvokeRequired)
        {
            this.Invoke((Action<TextBox, string>)AppendTextInBox, OutputTextBox, text);
        }
        else
        {
            box.Text += text;
        }
    }

    private void InterProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        AppendTextInBox(OutputTextBox, outLine.Data + Environment.NewLine);
    }

    private void Enterbutton_Click(object sender, EventArgs e)
    {
        InterProc.StandardInput.WriteLine(CommandTextBox.Text);
    }

So, I moved the BeginOutputReadLine to just after the process is started. That ensures it's really only called once. I also did an invoke required to clean up thread calls. Hopefully this should work for you.

Solution 2

The best solution I have found is:

private void Redirect(StreamReader input, TextBox output)
{
    new Thread(a =>
    {
        var buffer = new char[1];
        while (input.Read(buffer, 0, 1) > 0)
        {
            output.Dispatcher.Invoke(new Action(delegate
            {
                output.Text += new string(buffer);
            }));
        };
    }).Start();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            CreateNoWindow = true,
            FileName = "app.exe",
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            UseShellExecute = false,
        }
    };
    if (process.Start())
    {
        Redirect(process.StandardError, textBox1);
        Redirect(process.StandardOutput, textBox1);
    }
}

Solution 3

I've used code something like this:

    public static void Run(string fileName, string arguments, out string standardOutput, out string standardError, out int exitCode)
    {
        Process fileProcess = new Process();
        fileProcess.StartInfo = new ProcessStartInfo
        {
            FileName = fileName,
            Arguments = arguments,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            UseShellExecute = false,
            WindowStyle = ProcessWindowStyle.Hidden,
            CreateNoWindow = true,
        };

        bool started = fileProcess.Start();

        if (started)
        {
            fileProcess.WaitForExit();
        }
        else
        {
            throw new Exception("Couldn't start");
        }

        standardOutput = fileProcess.StandardOutput.ReadToEnd();
        standardError = fileProcess.StandardError.ReadToEnd();
        exitCode = fileProcess.ExitCode;

    }

But it's not interactive. But if the app is interactive, it'll take a lot more code anyway.

Share:
17,552
Zach
Author by

Zach

0xE year old C# developer, learning Python, as well as Django.

Updated on June 15, 2022

Comments

  • Zach
    Zach almost 2 years

    I have a form with 3 controls:

    1. A textbox for the user to enter commands to send to a console application,
    2. A button to confirm the commands to be sent and
    3. A read-only textbox to display the output from the application.

    What I want is for the user to enter commands in the first textbox, press the button to enter and receive feedback via the second textbox.

    I know how to use ProcessStartInfo.RedirectStandardOutput but, however, the app hangs when I use StandardOutput.ReadToEnd().

    I had a look at the asynchronous Process.BeginOutputReadLine() but, even though my app does not hang, somehow I get no response in the textbox, it does absolutely nothing.

    Here's my code:

    public partial class MainForm : Form
    {
    
        private void MainForm_Load(object sender, EventArgs e)
        {
            InitializeInterpreter();
        }
    
        private void InitializeInterpreter()
        {
            InterProc.StartInfo.UseShellExecute = false;
            InterProc.StartInfo.FileName = "app.exe";
            InterProc.StartInfo.RedirectStandardInput = true;
            InterProc.StartInfo.RedirectStandardOutput = true;
            InterProc.StartInfo.RedirectStandardError = true;
            InterProc.StartInfo.CreateNoWindow = true;
            InterProc.OutputDataReceived += new DataReceivedEventHandler(InterProcOutputHandler);
    
            InterProc.Start();
        }
    
        private static void InterProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
               OutputTextBox.Append(Environment.NewLine + outLine.Data);
            }
        }
    
        private void Enterbutton_Click(object sender, EventArgs e)
        {
            InterProc.StandardInput.Write(CommandtextBox.Text);
            InterProc.BeginOutputReadLine();
        }
    }
    

    Is there any way I can have this run smoothly? Thanks.