3

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

  • 当父级是控制台应用程序时,一切正常。
  • 当父级是 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”或
  • 父母根本没有重定向标准输出
4

1 回答 1

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

如果你使用lockaround AllocConsole// StartFreeConsole你甚至可以启动多个子进程,每个子进程都有自己的控制台窗口。


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

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

不幸的是,这并没有在 .NET API 中公开。Process.Start如果你让它使用ShellExecute,它会做正确的事情,但是你不能重定向标准输出。我认为您尝试做的事情是不可能的System.Diagnostics.Process,您将不得不 P/Invoke CreateProcess

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

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

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

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

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

于 2013-04-07T15:55:52.320 回答