Search code examples
c#processstartinforedirectstandardoutput

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


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.


Solution

  • 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.