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);
}
}
}
我希望即使父应用程序是窗口应用程序,应用程序也能运行而不会崩溃,因为孩子应该有自己的控制台,并且我不会为此重定向输入。顺便说一句 - 在以下情况下一切正常:
- 孩子没有在做"读取键"或
- 父级根本没有重定向标准输出
既然当父进程是控制台应用程序时它可以正常工作,为什么不暂时将其转换为控制台应用程序呢?
[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 中公开。 如果你让它使用ShellExecute
,Process.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。我不确定是否有办法获取创建控制台后有效的句柄。答:不要这样,只需早点创建控制台(请参阅开头的新解决方案(。