如果我的父亲是一个窗口应用程序并且它正在重定向我的标准输出,C#:不能读取密钥

时间:2013-04-07 08:21:32

标签: c# .net io-redirection

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

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

我在这里缺少什么?

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

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

父应用代码:(将项目设置为Windows应用)

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

我期待应用程序在没有崩溃的情况下运行,即使父级是一个窗口应用程序,因为孩子应该拥有自己的控制台,而我不会重定向输入。 顺便说一句 - 一切都有效:

  • 孩子没有做“ReadKey”或
  • 父母没有重定向stdout

1 个答案:

答案 0 :(得分:1)

由于父进程是控制台应用程序时它可以正常工作,为什么不暂时将其转换成一个?

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

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


这是一个较旧的“解决方案”,无法正常运行。

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

不幸的是,这不会在.NET API中公开。如果您使用Process.StartShellExecute会做正确的事情,但是您无法重定向stdout。我认为System.Diagnostics.Process无法完成你想做的事情,你必须P / Invoke CreateProcess

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

我已经使用ProcessRunner测试了您的示例,如果父进程是控制台应用程序,它会按预期工作 - 子进程拥有自己的新控制台窗口(不会将父进程重用为{{}它可以System.Diagnostics.Process,并且stdout会被重定向到父级。

但是如果父进程是一个Windows应用程序,那么它会崩溃 - ReadKey()失败;并且ReadKey()输出也不可见。 (这与stderr完全相同,我只是没有意识到System.Diagnostics.Process也没有用。原因是你不能只重定向其中一个流 - 如设置stderr后,所有流都会重定向。

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

但是如果在Windows应用程序中调用GetStdHandle,则返回INVALID_HANDLE。我不确定是否有办法获得一个创建控制台后有效的句柄。 答:不要这样做,只需提前创建控制台(参见开头的新解决方案)。