Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run Process in Current Console

I'm writing a basic shell for Windows, and I was wondering if there is any way to run a subprocess (Process process) so that it uses the current console window. By this I mean that I do not want to redirect input/output; I want the process to take input from the current console and print output directly to the same console window.

The reason is that I want to allow this subprocess to set console colors for output, which cannot happen if I redirect the process' standard output. Also, I currently use the code

while (!process.HasExited)
    process.StandardInput.WriteLine(Console.ReadLine());

to redirect standard input to the process. However, if the process exits immediately after an input (for example, I type "exit" + ENTER, and the process exits), this loop will run once more, so the console is waiting for input from the user that will never be used by the process (it is about to exit).

So, long question short, how do I run a process in the current console, so that it can set console colors and directly take input from the console?

Edit: Below are the methods that are relevant to this question from my code:

static int runExe(string exePath, params string[] args)
{
    ProcessStartInfo startInfo = new ProcessStartInfo(exePath, args)
    {
        ErrorDialog = false,
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        RedirectStandardInput = true
    };
    Process process = new Process() { StartInfo = startInfo };
    process.Start();
    ReadThreadState stdout = readThread(process.StandardOutput, false);
    ReadThreadState stderr = readThread(process.StandardError, true);
    while (!process.HasExited)
        process.StandardInput.WriteLine(Console.ReadLine());
    stdout.stop = stderr.stop = true;
    return process.ExitCode;
}
class ReadThreadState
{
    public bool stop;
}
private static ReadThreadState readThread(StreamReader reader, bool isError)
{
    ReadThreadState state = new ReadThreadState();
    new Thread(() =>
    {
        while (!state.stop)
        {
            int current;
            while ((current = reader.Read()) >= 0)
                if (isError)
                    writeError(((char)current).ToString(), ConsoleColor.Red);
                else
                    Console.Write((char)current);
        }
    }).Start();
    return state;
}
like image 585
feralin Avatar asked Jul 19 '13 17:07

feralin


2 Answers

You need to create a ProcessStartInfo and set UseShellExecute to false:

var info = new ProcessStartInfo("program.exe", "arguments");
info.UseShellExecute = false;
var proc = Process.Start(info);
proc.WaitForExit();

That will start your program in the same console.

Working program that uses the above technique:

private static void Main(string[] args)
{
    Console.WriteLine("Starting program");
    var saveColor = Console.BackgroundColor;
    Console.BackgroundColor = ConsoleColor.Blue;
    var info = new ProcessStartInfo("cmd", "/c time");
    info.UseShellExecute = false;
    var proc = Process.Start(info);
    proc.WaitForExit();

    Console.BackgroundColor = saveColor;
    Console.WriteLine("Program exited");
    Console.ReadLine();
}

When you run the program, it starts a new copy of cmd.exe and runs the time command, which asks for input. I just used cmd.exe as an example here to illustrate. Any program that reads from standard input will work. Note also that the console colors work correctly.

like image 76
Jim Mischel Avatar answered Sep 20 '22 11:09

Jim Mischel


Jim Mischel's answer works like a charm. I'm moving some batch file processing into C# and that works great. You may find this small utility method handy. Just takes a command line, and runs it just like CALL in a batch file. Just stuff it in a utility class:

public static void RunCmd(string command) {
    Process.Start(new ProcessStartInfo("cmd.exe", "/c " + command) {
        UseShellExecute = false
    }).WaitForExit();            
}    
like image 38
Wade Hatler Avatar answered Sep 19 '22 11:09

Wade Hatler