4

Alright, first some background information. I've written an application to have a real-time connection with the cmd.exe

The problem: Instead of writing once to a Process, I want to write multiple times without closing the cmd.exe. This causes an error because I have to close the StreamWriter before being able to retrieve any output and after I've recieved output, I want to write to it again.

Example: I want to give a command cd C:\ and recieve the output with the changed path. After that I want to see which files are inside the C:\ directory by using dir. I don't want the process to restart, because it resets the path.

I know I can simply use dir C:\, but that's not the point. This is only a brief example, I want to use this for many other things that require this problem to be solved.

class Program
    {
        static void Main(string[] args)
        {
            ProcessStartInfo pInfo = new ProcessStartInfo();
            pInfo.RedirectStandardInput = true;
            pInfo.RedirectStandardOutput = true;
            pInfo.RedirectStandardError = true;
            pInfo.UseShellExecute = false;
            pInfo.CreateNoWindow = true;
            pInfo.FileName = "cmd.exe";

            Process p = new Process();
            p.StartInfo = pInfo;

            bool pStarted = false;

            Console.Write("Command: ");
            string command = Console.ReadLine();

            while (command != "exit")
            {
                if (!pStarted && command == "start")
                {
                    p.Start();
                    Console.WriteLine("Process started.");

                    pStarted = true;
                }
                else if (pStarted)
                {
                    StreamWriter sWriter = p.StandardInput;
                    if (sWriter.BaseStream.CanWrite)
                    {
                        sWriter.WriteLine(command);
                    }
                    sWriter.Close();

                    string output = p.StandardOutput.ReadToEnd();
                    string error = p.StandardError.ReadToEnd();

                    Console.WriteLine("\n" + output + "\n");
                }

                Console.Write("\nCommand: ");
                command = Console.ReadLine();
            }

            Console.WriteLine("Process terminated.");
            Console.ReadKey();
            }
    }

Does anyone know how I can hold on to a process and write multiple times to it, each time recieving the output.

Thanks in advance.

Edit : This might or might not be slightly identical to the following question : Execute multiple command lines with the same process using .NET. The question linked has no useful answer and is diffrent in multiple ways. One huge problem I'm facing is that I want the output printed after every command the user sends.

7
  • 1
    "have to close the StreamWriter " - why? Standard way to communicating to CMD using events (or separate thread to read/write) should work fine (assuming you are showing very simplified code)... Commented Sep 30, 2015 at 19:29
  • 1
    Possible duplicate of Execute multiple command lines with the same process using .NET Commented Sep 30, 2015 at 19:32
  • @EmpereurAiman The linked question has no answer. Commented Sep 30, 2015 at 20:34
  • @EmpereurAiman My main problem is that I'm looking to print out the output from my cmd after every command the user sends. Commented Sep 30, 2015 at 21:03
  • But you can read multiple times as well as write multiple times. Commented Sep 30, 2015 at 21:15

1 Answer 1

1

You are closing the stream and you can't simply create a new stream, so

  1. declare sWriter outside the loop
  2. don't close the stream

Unfortunately, you can then not use ReadToEnd() any more, since that would wait until the process terminates, which cmd doesn't. Therefore, you have to

  1. read the console output asynchronously.

This again, results in problems with the output, since your output "Command:" may interfere with the output of cmd.

The following async version of your program should solve your basic problem, but will leave you with the mixed output:

using System;
using System.Diagnostics;
using System.IO;

namespace MultiConsole
{
    class Program
    {
        private static void Main()
        {
            var pInfo = new ProcessStartInfo
            {
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = false,
                FileName = "cmd.exe"
            };

            var p = new Process {StartInfo = pInfo};

            bool pStarted = false;

            Console.Write("Command: ");
            string command = Console.ReadLine();
            StreamWriter sWriter = null;
            while (command != "exit")
            {
                if (!pStarted && command == "start")
                {
                    p.Start();
                    sWriter = p.StandardInput;
                    pStarted = true;
                    ConsumeConsoleOutput(p.StandardOutput);
                    Console.WriteLine("Process started.");
                }
                else if (pStarted)
                {
                    if (sWriter.BaseStream.CanWrite)
                    {
                        sWriter.WriteLine(command);
                    }
                }

                Console.Write("\nCommand: ");
                command = Console.ReadLine();
            }

            if (sWriter != null) sWriter.Close();
            Console.WriteLine("Process terminated.");
            Console.ReadKey();
        }

        private static async void ConsumeConsoleOutput(TextReader reader)
        {
            var buffer = new char[1024];
            int cch;

            while ((cch = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                Console.Write(new string(buffer, 0, cch));
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Apparantly the "async" namespace can not be used in VS2010, so I might need to install some libraries to get this working. But thank you very much! It should fix the problem.
@Shwasted: VS2010? Don't you want to use 2015 Community Edition? It has almost as many features as 2013 Professional.
First of all, sorry for the late answer. When trying to run your code, I recieve an error : "'TextReader' does not contain a definition for 'ReadAsync' and no extension method 'ReadAsync' accepting a first argument of type 'TextReader' could be found (are you missing a using directive or an assembly reference?)" NOTE : I've included all libraries you're using and I'm using .NET FrameWork 4.5 so according to msdn.microsoft.com/en-us/library/… it should be good...
@Shwasted: I also tried in VS2013 and set the the target framework to 4.5 - and it works. You just need 2 references: System and System.Core.
Problem solved... I'm apprantly an idiot, when transporting my VS2010 project to VS2015 I didn't change the framework to 4.5.

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.