在当前控制台中运行进程

16

我正在为Windows编写一个基本的shell,我想知道是否有办法运行一个子进程(Process process) 以便它使用当前控制台窗口。我的意思是我不想重定向输入/输出;我希望该进程从当前控制台获取输入并直接将输出打印到同一控制台窗口。

原因是我希望允许此子进程设置输出的控制台颜色,如果我重定向了进程的标准输出,这是不可能发生的。另外,我目前使用以下代码:

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

将标准输入重定向到进程。然而,如果进程在接收到输入后立即退出(例如,我输入“exit”+ ENTER,进程退出),此循环将再次运行,因此控制台正在等待用户的输入,但该进程不会使用它(它即将退出)。

所以,简单地说,我如何在当前控制台中运行进程,使其可以设置控制台颜色并直接从控制台获取输入

编辑:下面是我的代码中与此问题相关的方法:

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;
}

尝试使用 cmd.exe/c 选项进行实验。例如 Process.Start("cmd.exe", "YOURAPP.exe /c"); - Anri
@Anri 我不认为那是我需要的;我不想通过 cmd.exe 运行它。 - feralin
通过命令行启动控制台应用程序与否几乎没有区别。 - Anri
这个要求有点令人警惕。这意味着另一个应用程序应该是一个.dll文件,您在程序中引用它,而不是另一个可执行程序。 - Servy
@Servy 我正在编写一个带有颜色的命令行界面。如果我从该界面输入“shell”并按Enter键,则子shell将没有颜色。我希望子shell也能够具有颜色。但是,我无法为shell将运行的每个程序引用每个.dll文件;有些程序甚至可能不是.NET程序... - feralin
2个回答

24
你需要创建一个 ProcessStartInfo 并将 UseShellExecute 设置为 false:
var info = new ProcessStartInfo("program.exe", "arguments");
info.UseShellExecute = false;
var proc = Process.Start(info);
proc.WaitForExit();

这将在同一控制台中启动您的程序。

使用上述技术的工作程序:

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();
}
当你运行该程序时,它会启动一个新的cmd.exe副本并运行time命令,该命令会要求输入。这里只是举例说明,使用任何从标准输入读取的程序都可以。还要注意控制台颜色正确工作。

我已经做了这件事(也许我应该发布更多的代码),但子进程从控制台中从未请求输入。这不是答案 :( - feralin
@feralin:你可能想试试我的例子。我不知道你做了什么,但是我的示例可以实现你所说的需求。 - Jim Mischel
@feralin:你问:“如何在当前控制台中运行进程,以便可以设置控制台颜色并直接从控制台获取输入?”我的示例正是这样做的。 - Jim Mischel
你说得对。我本来以为我之前试过了,但我猜我没有。我的错 :(。你的答案是正确的。 - feralin
1
还要注意 OP 代码中显式 true 与隐式的 CreateNoWindow=false 的默认值。这里是一份非常棒的答案。 - Matthew Benedict

2

Jim Mischel的回答非常好用。我正在将一些批处理文件处理转移到C#中,这很棒。您可能会发现这个小工具方法很方便。只需要一个命令行,就像在批处理文件中调用一样运行它。只需将其放入实用程序类中:

public static void RunCmd(string command) {
    Process.Start(new ProcessStartInfo("cmd.exe", "/c " + command) {
        UseShellExecute = false
    }).WaitForExit();            
}    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接