How to make a C # shell around a console application (any language)

I am trying to create a console application that can cover almost any application that opens a console.

A few quick facts:

  • The output of the wrapped application should be displayed on the console.
  • The input of the wrapper console must also be the input of the console application.
  • You should be able to use code to insert commands.

This is what I have so far, everything works, except for the last few lines:

ProcessStartInfo startInfo = new ProcessStartInfo("someBatchThingy.cmd"); startInfo.WorkingDirectory = Environment.CurrentDirectory; startInfo.UseShellExecute = false; Process process = new Process(); process.StartInfo = startInfo; process.Start(); Thread.Sleep(5000); Stream s = Console.OpenStandardOutput(); byte[] stopCommand = new byte[] { 115, 116, 111, 112, 13 }; s.Write(stopCommand, 0, stopCommand.Length); process.WaitForExit(); 

Since performance matters a lot, I really want to reassign where the process is displayed, and not manually transfer data from the hidden console to the shell console.

Does anyone know how to do this / if possible?

+4
source share
3 answers

So, as far as I know, there is no way to achieve this without pushing data through your application.

The code for this is as follows:

 using System; using System.Diagnostics; using System.IO; using System.Threading; namespace TestApp { internal static class Program { [MTAThread] public static void Main(string[] args) { const string fileName = @"..\..\..\ChildConsoleApp\bin\Debug\ChildConsoleApp.exe"; // Fires up a new process to run inside this one var process = Process.Start(new ProcessStartInfo { UseShellExecute = false, RedirectStandardError = true, RedirectStandardInput = true, RedirectStandardOutput = true, FileName = fileName }); // Depending on your application you may either prioritize the IO or the exact opposite const ThreadPriority ioPriority = ThreadPriority.Highest; var outputThread = new Thread(outputReader) { Name = "ChildIO Output", Priority = ioPriority}; var errorThread = new Thread(errorReader) { Name = "ChildIO Error", Priority = ioPriority }; var inputThread = new Thread(inputReader) { Name = "ChildIO Input", Priority = ioPriority }; // Set as background threads (will automatically stop when application ends) outputThread.IsBackground = errorThread.IsBackground = inputThread.IsBackground = true; // Start the IO threads outputThread.Start(process); errorThread.Start(process); inputThread.Start(process); // Demonstrate that the host app can be written to by the application process.StandardInput.WriteLine("Message from host"); // Signal to end the application ManualResetEvent stopApp = new ManualResetEvent(false); // Enables the exited event and set the stopApp signal on exited process.EnableRaisingEvents = true; process.Exited += (e, sender) => { stopApp.Set(); }; // Wait for the child app to stop stopApp.WaitOne(); // Write some nice output for now? Console.WriteLine(); Console.Write("Process ended... shutting down host"); Thread.Sleep(1000); } /// <summary> /// Continuously copies data from one stream to the other. /// </summary> /// <param name="instream">The input stream.</param> /// <param name="outstream">The output stream.</param> private static void passThrough(Stream instream, Stream outstream) { byte[] buffer = new byte[4096]; while (true) { int len; while ((len = instream.Read(buffer, 0, buffer.Length)) > 0) { outstream.Write(buffer, 0, len); outstream.Flush(); } } } private static void outputReader(object p) { var process = (Process)p; // Pass the standard output of the child to our standard output passThrough(process.StandardOutput.BaseStream, Console.OpenStandardOutput()); } private static void errorReader(object p) { var process = (Process)p; // Pass the standard error of the child to our standard error passThrough(process.StandardError.BaseStream, Console.OpenStandardError()); } private static void inputReader(object p) { var process = (Process)p; // Pass our standard input into the standard input of the child passThrough(Console.OpenStandardInput(), process.StandardInput.BaseStream); } } } 

My child app code looks like this:

 using System; namespace ChildConsoleApp { internal static class Program { public static void Main() { Console.WriteLine("Hi"); string text; // Echo all input while ((text = Console.ReadLine()) != "stop") Console.WriteLine("Echo: " + text); Console.WriteLine("Stopped."); } } } 

As always, this has some overhead, although this is probably negligible in any non-trivial application that you wrap.

If you send a large amount of data and want to avoid flushing more than necessary, you can increase the buffer size from 4 KB to what suits you.

+2
source

An old question, but just came up with the following solution to a similar problem.

 public class ConsoleAppWrapper { private Process _process; public ConsoleAppWrapper(string exeFilename) { // Start the Player console app up with hooks to standard input/output and error output _process = new Process() { StartInfo = new ProcessStartInfo(exeFilename) { UseShellExecute = false, RedirectStandardError = true, RedirectStandardInput = true, RedirectStandardOutput = true } }; _process.Start(); } public StreamReader StandardOutput => _process.StandardOutput; public StreamReader StandardError => _process.StandardError; public StreamWriter StandardInput => _process.StandardInput; ~ConsoleAppWrapper() { // When I used this class to help with some SpecFlow feature tests of a console app I found it wasn't destroying the console window correctly when assertions failed // using a destructor ensures the windows is destroyed when the wrapper goes out of scope. Kill(); } public void Kill() { Dispose(); } } 

using this class, you can control the interaction without a console application.

  var c = new ConsoleAppWrapper("App.exe"); Assert.That(c.StandardOutput.ReadLine(), Is.StringContaining("Enter some data:")); c.StandardInput.WriteLine("SOME DATA\n"); Assert.That(c.StandardOutput.ReadLine(), !Is.StringContaining("Error")); 
+1
source

Its new will be beautiful. But at least it does not depend on sendkeys.

  var process = new Process { StartInfo = new ProcessStartInfo { FileName = "myLegacyConsoleApp.exe", UseShellExecute = false, RedirectStandardOutput = true, WorkingDirectory = @"C:\Program Files\myAppDirectory\", } }; process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived); process.Start(); process.BeginOutputReadLine(); // You need to wait a bit.. Im sorry.. System.Threading.Thread.Sleep(5000); process.StandardInput.WriteLine("Welcome to Γ›berland"); process.WaitForExit(); 
0
source

All Articles