C#:如果我的父级是窗口应用程序并且它正在重定向我的标准输出,则无法读取键

本文关键字:我的 标准输出 重定向 读取 如果 窗口 应用程序 | 更新日期: 2023-09-27 18:07:45

我正在尝试从子控制台应用程序捕获输出。

  • 当父级是控制台应用程序时,一切正常。
  • 当父级是Windows应用程序时,子项无法运行,但Console.ReadKey()说当stdInput被重定向时无法读取Key。但是,我没有重定向输入(仅重定向输出(。

我在这里错过了什么?

子代码(将项目设置为控制台应用(:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ChildProcess
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            char key = Console.ReadKey().KeyChar;
            Console.Out.WriteLine("stdout");
            Console.Error.WriteLine("stderr");
        }
    }
}

父应用代码:(将项目设置为窗口应用(

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RedirectProcessOutput
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string fileName = @"ChildPRocess.exe";
            string arg = "i";
            string processOutput = "?";
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = false;
            p.StartInfo.FileName = fileName;
            p.StartInfo.Arguments = arg;
            p.Start();
            processOutput = p.StandardOutput.ReadToEnd();
            p.WaitForExit();
            Console.WriteLine("The child process output is:" + processOutput);
        }
    }
}

我希望即使父应用程序是窗口应用程序,应用程序也能运行而不会崩溃,因为孩子应该有自己的控制台,并且我不会为此重定向输入。顺便说一句 - 在以下情况下一切正常:

  • 孩子没有在做"读取键"或
  • 父级根本没有重定向标准输出

C#:如果我的父级是窗口应用程序并且它正在重定向我的标准输出,则无法读取键

既然当父进程是控制台应用程序时它可以正常工作,为什么不暂时将其转换为控制台应用程序呢?

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FreeConsole();
private static void Main(string[] args)
{
    Process p = new Process();
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.CreateNoWindow = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = false;
    p.StartInfo.FileName = "Child.exe";
    p.StartInfo.Arguments = "i";
    AllocConsole();
    p.Start();
    FreeConsole();
    string processOutput = p.StandardOutput.ReadToEnd();
    p.WaitForExit();
    Debug.WriteLine("The child process output is:" + processOutput);
}

如果在 AllocConsole/Start/FreeConsole 周围使用lock,您甚至可以启动多个子进程,每个子进程都有自己的控制台窗口。


这是一个较旧的"解决方案",无法正常工作。

看看 CreateProcess 标志。不设置CREATE_NO_WINDOW不会为您提供新的控制台对象;你需要CREATE_NEW_CONSOLE。[不,这是错误的;见下文]

遗憾的是,这不会在 .NET API 中公开。 如果你让它使用ShellExecuteProcess.Start会做正确的事情,但你不能重定向 stdout。我认为你想做的事情是不可能用System.Diagnostics.Process,你将不得不P/调用CreateProcess

就在昨天,我编写了自己的System.Diagnostics.Process替代方案,因为我需要 MS 实现未提供的另一个功能 - 将 stdout 和 stderr 重定向到同一流。随意重用我的代码,但请记住,它还没有经过太多测试。

我已经用我的ProcessRunner测试了您的示例,如果父进程是控制台应用程序,它就会按预期工作 - 子进程获得自己的新控制台窗口(不像System.Diagnostics.Process那样重用父程序的控制台窗口(,它可以从中ReadKey(),并且 stdout 被重定向到父进程。

但是,如果父进程是Windows应用程序,则这会导致故障 - ReadKey()失败;并且stderr输出也不可见。(这与System.Diagnostics.Process的行为完全相同,我只是没有意识到stderr也不起作用。这样做的原因是您不能只重定向其中一个流 - 一旦设置了STARTF_USESTDHANDLES,所有流都会被重定向。

现在我不确定如果父应用程序是控制台,为什么它会起作用。也许重定向的 stdin/stderr 句柄指的是当前控制台,而不是调用 GetStdHandle 时当前的控制台。

但是,如果在 Windows 应用程序中调用GetStdHandle,它将返回INVALID_HANDLE。我不确定是否有办法获取创建控制台后有效的句柄。答:不要这样,只需早点创建控制台(请参阅开头的新解决方案(。