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.

Community
  • 1
  • 1
Pieter Moens
  • 331
  • 1
  • 4
  • 14
  • 1
    "have to close the StreamWriter " - why? [Standard way to communicating to CMD](http://stackoverflow.com/questions/206323/how-to-execute-command-line-in-c-get-std-out-results) using events (or separate thread to read/write) should work fine (assuming you are showing very simplified code)... – Alexei Levenkov Sep 30 '15 at 19:29
  • 1
    Possible duplicate of [Execute multiple command lines with the same process using .NET](http://stackoverflow.com/questions/437419/execute-multiple-command-lines-with-the-same-process-using-net) – wingerse Sep 30 '15 at 19:32
  • @EmpereurAiman The linked question has no answer. – Tamir Vered Sep 30 '15 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. – Pieter Moens Sep 30 '15 at 21:03
  • But you can read multiple times as well as write multiple times. – BugFinder Sep 30 '15 at 21:15
  • @BugFinder That's what I'm trying... within the same process ofcourse. As soon as I send a second comment I recieve an error. Try to run my code and you'll see. – Pieter Moens Sep 30 '15 at 21:18
  • catching output when it happens may be done by hooking into the right events. See [here](http://stackoverflow.com/questions/22289288/how-can-i-feed-commands-to-cmd-exe-process-via-an-input-stream-manually?s=1|1.9373) for a (rather confusing) example. – TaW Oct 01 '15 at 18:32

1 Answers1

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));
            }
        }
    }
}
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
  • 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. – Pieter Moens Oct 01 '15 at 09:42
  • @Shwasted: VS2010? Don't you want to use 2015 Community Edition? It has almost [as many features as 2013 Professional](http://superuser.com/questions/947923/upgrading-from-visual-studio-2013-professional-to-visual-studio-2015-community). – Thomas Weller Oct 01 '15 at 18:40
  • 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 https://msdn.microsoft.com/en-us/library/system.io.textreader.readasync%28v=vs.110%29.aspx it should be good... – Pieter Moens Oct 06 '15 at 22:28
  • @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`. – Thomas Weller Oct 07 '15 at 17:53
  • 1
    Problem solved... I'm apprantly an idiot, when transporting my VS2010 project to VS2015 I didn't change the framework to 4.5. – Pieter Moens Oct 07 '15 at 20:24