2

I want to perform some command lines to display the result after each input.

    Process p = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;
    p.StartInfo = info;
    p.Start();
    using (StreamWriter sw = p.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
        {
            sw.WriteLine("ftp");
            //output
            sw.WriteLine("open ftp.server.com");
            //output
            sw.WriteLine("username");
            //output
            sw.WriteLine("password");
            //output
        }
    }

Help me to understand how to make the output result after each sw.WriteLine(...)?

Updated

It is not working with ftp. Why?

Initialization:

Test test = new Test();
test.start();
Console.ReadKey();

Class Test:

class Test
    {    
        static StringBuilder StdOutput = new StringBuilder();
        Process p = null;
        Queue<string> cmdQueue = new Queue<string>();

        public void start(){

            cmdQueue = new Queue<string>();
            cmdQueue.Enqueue("cd c:\\");
            cmdQueue.Enqueue("dir");

            cmdQueue.Enqueue("ftp");
            cmdQueue.Enqueue("open us1.hostedftp.com");
            cmdQueue.Enqueue("[email protected]");
            cmdQueue.Enqueue("123456");
            cmdQueue.Enqueue("dir");
            setupProcess();
            startProcess();    
        }

        private void setupProcess()
        {
            p = new Process();
            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = "cmd";
            info.CreateNoWindow = true;
            info.RedirectStandardOutput = true;
            info.RedirectStandardInput = true;
            info.UseShellExecute = false;

            p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);

            StdOutput = new StringBuilder();

            p.StartInfo = info;
        }

        private async void startProcess()
        {
            p.Start();    
            p.BeginOutputReadLine();                    

            using (StreamWriter sw = p.StandardInput)
            {

                if (sw.BaseStream.CanWrite)
                {
                    while (cmdQueue.Count > 0)
                    { 
                            string cmd = cmdQueue.Dequeue();

                            if (cmd != null & cmd != "")
                            {
                                await sw.WriteLineAsync(cmd);

                                Thread.Sleep(100);

                                //System.Console.WriteLine(StdOutput);
                            }
                            else
                            {
                                break;
                            }  
                    }

                    Console.WriteLine(StdOutput);    
                }                       
                p.WaitForExit();                    
            }
        }

        private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                StdOutput.Append(Environment.NewLine + outLine.Data);    
                //System.Console.WriteLine(Environment.NewLine + outLine.Data);    
            }
        }
    }
0

1 Answer 1

2

I assume that you are actually asking about how to catch outputs from all the commands you want to have executed in the (one) process.

Here is a version of a solution I came up with a long time ago, when I was a rookie here..

The trick is to collect the output as is comes along by listening to events the Process will trigger whenever output gets created: OutputDataReceived and ErrorDataReceived. We need to run things async for this to work, so it will look a little more complicated than the usual examples, which only have one process executing one command..:

First a few variables:

    Queue<string> cmdQueue = new Queue<string>();
    static StringBuilder StdOutput = new StringBuilder();
    static StringBuilder ErrOutput = new StringBuilder();

    Process p = null;
    Task processTask = null;
    bool processIsRunning = false;

Here is a button click event that starts processing all commands from a multiline TextBox. Output gets collected in the two StringBuilders; when the queue is empty, I wait a little longer..:

private void button1_Click(object sender, EventArgs e)
{
    cmdQueue = new Queue<string>(tb_commands.Lines.ToList());

    setupProcess();
    startProcessTask();

    while (cmdQueue.Count > 0) Thread.Sleep(100);
    Thread.Sleep(500);
    tb_out.AppendText(StdOutput + "\r\n" + ErrOutput + "\r\n");
}

Here is the routine that set up the Process. Here we register two events that will notify us when there are lines in the output streams..:

private void setupProcess()
{
    p = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
    info.CreateNoWindow = true;
    info.RedirectStandardOutput = true;
    info.RedirectStandardError = true;
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;

    p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
    p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataHandler);
    StdOutput = new StringBuilder();
    ErrOutput = new StringBuilder();
    p.StartInfo = info;
}

After the setup we can start a Task that will start our Process asynchonously..:

private void startProcessTask()
{
    var task = Task.Factory.StartNew(() => startProcess());
    processTask = task;
}

..and finally here is the async method that after starting the Process and beginning with the asynchronous read operations on the redirected streams, keeps feeding it all lines from the command queue.

private async void startProcess()
{
    try { p.Start(); processIsRunning = true; } catch
    {
        ErrOutput.Append("\r\nError starting cmd process.");
        processIsRunning = false;
    }

    p.BeginOutputReadLine();
    p.BeginErrorReadLine();

    using (StreamWriter sw = p.StandardInput)
    {

        if (sw.BaseStream.CanWrite)
            do
            {
                try
                {
                    string cmd = cmdQueue.Dequeue();
                    if (cmd != null & cmd != "") await sw.WriteLineAsync(cmd);
                } catch { }
            } while (processIsRunning);
        try { p.WaitForExit(); } catch { ErrOutput.Append("WaitForExit Error.\r\n"); }
    }
}

The last pieces are the two events we have registered for reading the output from the two streams and adding them to the StringBuilders:

private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
    if (!String.IsNullOrEmpty(outLine.Data))
    {  
        StdOutput.Append(Environment.NewLine + outLine.Data);
    }
}

private static void ErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
    if (!String.IsNullOrEmpty(outLine.Data))
    {  
        ErrOutput.Append(Environment.NewLine + outLine.Data);
    }
}

Note that this works fine for all sorts of commands you can feed into the process, including FTP. Here I change my codepage, show the images I have before, log in to an FTP server, call up the help page, cd and dir, download an image, close the connection and check the images I have now..:

enter image description here

One Caveat: There must be something wrong in the way I wrote this, as VS keeps complaining about a System.InvalidOperationException and the exe file hogs ~10% cpu. Helping me out would be very much appreciated..

Sign up to request clarification or add additional context in comments.

5 Comments

Well, as you can see it is working here. Note that only output that is actually sent to stdout can be captured!
I updated the main post. I added an example.The ftp command is not working. Why? Please tell me.
Hm. I had no problems with ftp. (I'm on win8.1 and logged in with admin rights)) I guess you can feed your very same commands into a cmd window manually?
Hi, I have tested your version now and at least in the WinForms program I have it works just fine. The only changes were: using my ftp test commands, removing the Console.ReadKey(); and adding a System.Console.WriteLine( Environment.NewLine + outLine.Data); to the OutputDataHandler to show the output.. - I'll have to look into this again somt time, as your version somehow doesn't show the issues mine did. However your problems with ftp are probably coming from somewhere else..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.