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

4

2

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.

Defalcation answered 19/11, 2009 at 18:26 Comment(3)
<dumb comment>You know you can do this without redirecting standard in/out?</dumb comment> I assume you're testing how re-direction works, but you haven't stated that in your question at all.Thereinto
Can you please elaborate on that?Defalcation
By "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" do you actually mean "enter a command (which would normally run in a command prompt/"dos" window), run the command in the background, and show the commands output in the second text box"? If so ignore what I just said.Thereinto
M
7

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.

Marine answered 20/11, 2009 at 20:15 Comment(8)
Thanks a lot, but I hooked it up properly, but I still get no output in the output text box when I enter a command and press enter. The executable I'm trying to call is "lua.exe" from luaforwindows.luaforge.net Entering something like print("Hello") should output: Hello > and the user should be able to use it like that. Anyway, I hooked up some Debug.WriteLine()'s which showed that the last statement executed may have been InterProc.StandardInput.WriteLine(CommandTextBox.Text); so that may be where something goes wrong.Defalcation
So, I used this code, and it worked flawlessly for me. I did write a custom executable, that echoed everything back to me. The interactivity worked fine.Marine
If the executable isn't ready to receive a message, it won't get received yet. So, maybe the problem is in the lua.exe? Try hooking it up to a different app?Marine
It seems as though something's wrong with lua.exe, even with StdError. It works great with other exe's but not lua.exe. Oh well...Defalcation
@Marine @Defalcation The problem with this event is that it only is called when a the output receives a newline. My solution in another answer in this thread works also when the output has no carriage returns nor newlinesCleaning
hi @Marine I have a doubt if i have to input 3 values from 3 text boxes means how i can put the codeAcetylene
@Acetylene What would 3 text boxes even mean for this example? Are all of them getting sent to the same place?Marine
@Marine I tried sending three inputs to the console, at first time all 3 values sent to the first inputs and the remaining 2 values left emptied. Then i set time interval between them. Now its working.Acetylene
C
6

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);
    }
}
Cleaning answered 15/2, 2010 at 18:41 Comment(0)
M
1

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.

Marine answered 19/11, 2009 at 19:23 Comment(2)
By "Interactive" do you mean that you want to pass data to the standard in of the program? Or just that it will update as the app continues to output. The former is a bit harder than the latter. In any case, you complain that ReadToEnd is what hangs, when you're not using that in your sample code. What's actually the problem or can we see the new code?Marine
How to use this example?Gonzales
A
0

Where are you calling StandardOutput.ReadToEnd()? I once had a similar problem because I was calling Process.WaitForExit() before StandardOutput.ReadToEnd(). I had a large amount of input, and the output buffer was full before completion and my process was blocked.

You must call StandardOutput.ReadToEnd()before Process.WaitForExit().

Asthmatic answered 19/11, 2009 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.