在 C# 中创建实时输入和输出流到命令提示符
Creating Real-Time Input & Output Stream To Command Prompt in C#
一般问题
如何使用某种形式的 C# 应用程序(无论是 WinForms、WPF 还是 UWP)在命令提示符实例 (cmd.exe) 之间创建直接输入/输出流。有效地使用 CMD,就好像它只是一个已编译的库。
我真正需要的
我需要能够像直接使用应用程序一样读取和写入命令提示符实例,这意味着可行的解决方案需要能够:
- 以编程方式将输入作为命令写入 CMD 进程的实例(能够使用 C# 将
ping google.com
传递给 CMD)
- 以编程方式读取正在生成的 CMD 进程的输出(在
ping google.com
的情况下,我应该能够在生成时将每个单独的 ping 作为它自己的行来获取。换句话说:
Pinging google.com [172.217.6.14] with 32bytes of data:
Reply from 172.217.6.14: bytes=32 time=31ms TTL=117
Reply from 172.217.6.14: bytes=32 time=29ms TTL=117
Reply from 172.217.6.14: bytes=32 time=46ms TTL=117
Reply from 172.217.6.14: bytes=32 time=26ms TTL=117
Ping statistics for 172.217.6.14:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 26ms, Maximum = 35ms, Average = 29ms
这些代码块中的每一个都应该能够在生成时被单独引用,无论这意味着将它们写入文本框还是将它们输出到控制台逐行-行.
我试过的
我已经查看了这些线程,它们提供的答案与我需要的相似,但不完全相同:StandardOutput.ReadToEnd() hangs [duplicate], , Capturing console output from a .NET application (C#), & Reading from a process, StreamReader.Peek() not working as expected
为了简洁起见,此示例适用于控制台应用程序 (.NET Framework)。
我已经尝试使用 StreamReader.Peek()
方法创建一个简单的 while 循环来查看是否已到达流的末尾,尽管它有其自身的问题(即它不会动态更新,所以一旦CMDOutput.Peek()
达到 -1
它不会返回,即使从那时起更新了流)。下面是我用于此解决方案的代码示例:
ProcessStartInfo CMDStartInfo = new ProcessStartInfo(@"C:\Windows\System32\cmd.exe", "/c ping google.com");
CMDStartInfo.UseShellExecute = false;
CMDStartInfo.ErrorDialog = false;
CMDStartInfo.RedirectStandardError = true;
CMDStartInfo.RedirectStandardInput = true;
CMDStartInfo.RedirectStandardOutput = true;
Process CMD = new Process();
CMD.StartInfo = CMDStartInfo;
bool CMDStarted = CMD.Start();
StreamWriter CMDInput = CMD.StandardInput;
StreamReader CMDOutput = CMD.StandardOutput;
StreamReader CMDErrors = CMD.StandardError;
while (true)
{
while (CMDOutput.Peek() > -1)
{
try
{
Console.WriteLine(CMDOutput.ReadLine());
}
catch
{
return;
}
}
Console.WriteLine(CMDOutput.Peek());
CMDInput.WriteLine(Console.ReadLine());
}
同样,您可以看到此解决方案的主要问题是在第一行输出之后(Pinging google.com [172.217.6.14] with 32bytes of data:
)
CMDOutput.Peek()
returns -1
无限期,无法读取更多输出。
我尝试使用不同的方法,通过删除循环并将起始参数更改为 /K
,并使用 StreamReader.ReadToEnd()
代替 StreamReader.ReadLine()
,哪种方法可行?我现在可以读取多行,但它只运行一次,并且在使用 ping 命令时它根本不会输出。更不用说应用程序似乎也被挂在最后一行。
旁注:我尝试使用此方法获得的唯一多行输出来自无效命令。
Process CMD = new Process();
CMD.StartInfo.FileName = "cmd.exe";
CMD.StartInfo.Arguments = "/K";
CMD.StartInfo.UseShellExecute = false;
CMD.StartInfo.RedirectStandardInput = true;
CMD.StartInfo.RedirectStandardOutput = true;
CMD.StartInfo.WorkingDirectory = @"C:\";
CMD.Start();
StreamWriter CMDInput = CMD.StandardInput;
StreamReader CMDOutput = CMD.StandardOutput;
string InputString = Console.ReadLine();
CMDInput.WriteLine(InputString);
string OutputString = CMDOutput.ReadToEnd();
Console.WriteLine(OutputString);
经过多年的搜索和试验后,我只是不确定从这里该何去何从。
解决方案[柠檬天空]:
感谢 Lemon Sky 快速而简单的回复。对于遇到我的问题的任何其他人,可以在下面找到我用来解决它的代码。显然,我需要的确切事件已经存在一个事件侦听器,我所要做的就是像 Lemon Sky 所说的那样订阅它 (Process.OutputDataReceived
)。如果没有任何过滤,您仍然会收到包含您的命令的行,因此作为一个简单的修复,我添加了一些过滤来检测正在输出的行是否以 C:\
.
开头
Process CMD = new Process();
CMD.StartInfo.FileName = "cmd.exe";
CMD.StartInfo.Arguments = "/K";
CMD.StartInfo.UseShellExecute = false;
CMD.StartInfo.RedirectStandardInput = true;
CMD.StartInfo.RedirectStandardOutput = true;
CMD.StartInfo.WorkingDirectory = @"C:\";
CMD.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
if (e.Data.StartsWith(@"C:\"))
{
return;
}
else
{
Console.WriteLine(e.Data);
}
}
});
CMD.Start();
CMD.BeginOutputReadLine();
StreamWriter CMDInput = CMD.StandardInput;
while (true && !CMD.HasExited)
{
string InputString = Console.ReadLine();
if (InputString == "cls" || InputString == "clear")
Console.Clear();
else
CMDInput.WriteLine(InputString);
}
再次感谢柠檬天空!
设置process.StartInfo.RedirectStandardOutput = true
,订阅process.OutputDataReceived
,调用process.BeginOutputReadLine
。这将适用于由换行符分隔的基于文本的输出。
一般问题
如何使用某种形式的 C# 应用程序(无论是 WinForms、WPF 还是 UWP)在命令提示符实例 (cmd.exe) 之间创建直接输入/输出流。有效地使用 CMD,就好像它只是一个已编译的库。
我真正需要的
我需要能够像直接使用应用程序一样读取和写入命令提示符实例,这意味着可行的解决方案需要能够:
- 以编程方式将输入作为命令写入 CMD 进程的实例(能够使用 C# 将
ping google.com
传递给 CMD) - 以编程方式读取正在生成的 CMD 进程的输出(在
ping google.com
的情况下,我应该能够在生成时将每个单独的 ping 作为它自己的行来获取。换句话说:
Pinging google.com [172.217.6.14] with 32bytes of data:
Reply from 172.217.6.14: bytes=32 time=31ms TTL=117
Reply from 172.217.6.14: bytes=32 time=29ms TTL=117
Reply from 172.217.6.14: bytes=32 time=46ms TTL=117
Reply from 172.217.6.14: bytes=32 time=26ms TTL=117
Ping statistics for 172.217.6.14:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 26ms, Maximum = 35ms, Average = 29ms
这些代码块中的每一个都应该能够在生成时被单独引用,无论这意味着将它们写入文本框还是将它们输出到控制台逐行-行.
我试过的
我已经查看了这些线程,它们提供的答案与我需要的相似,但不完全相同:StandardOutput.ReadToEnd() hangs [duplicate],
为了简洁起见,此示例适用于控制台应用程序 (.NET Framework)。
我已经尝试使用 StreamReader.Peek()
方法创建一个简单的 while 循环来查看是否已到达流的末尾,尽管它有其自身的问题(即它不会动态更新,所以一旦CMDOutput.Peek()
达到 -1
它不会返回,即使从那时起更新了流)。下面是我用于此解决方案的代码示例:
ProcessStartInfo CMDStartInfo = new ProcessStartInfo(@"C:\Windows\System32\cmd.exe", "/c ping google.com");
CMDStartInfo.UseShellExecute = false;
CMDStartInfo.ErrorDialog = false;
CMDStartInfo.RedirectStandardError = true;
CMDStartInfo.RedirectStandardInput = true;
CMDStartInfo.RedirectStandardOutput = true;
Process CMD = new Process();
CMD.StartInfo = CMDStartInfo;
bool CMDStarted = CMD.Start();
StreamWriter CMDInput = CMD.StandardInput;
StreamReader CMDOutput = CMD.StandardOutput;
StreamReader CMDErrors = CMD.StandardError;
while (true)
{
while (CMDOutput.Peek() > -1)
{
try
{
Console.WriteLine(CMDOutput.ReadLine());
}
catch
{
return;
}
}
Console.WriteLine(CMDOutput.Peek());
CMDInput.WriteLine(Console.ReadLine());
}
同样,您可以看到此解决方案的主要问题是在第一行输出之后(Pinging google.com [172.217.6.14] with 32bytes of data:
)
CMDOutput.Peek()
returns -1
无限期,无法读取更多输出。
我尝试使用不同的方法,通过删除循环并将起始参数更改为 /K
,并使用 StreamReader.ReadToEnd()
代替 StreamReader.ReadLine()
,哪种方法可行?我现在可以读取多行,但它只运行一次,并且在使用 ping 命令时它根本不会输出。更不用说应用程序似乎也被挂在最后一行。
旁注:我尝试使用此方法获得的唯一多行输出来自无效命令。
Process CMD = new Process();
CMD.StartInfo.FileName = "cmd.exe";
CMD.StartInfo.Arguments = "/K";
CMD.StartInfo.UseShellExecute = false;
CMD.StartInfo.RedirectStandardInput = true;
CMD.StartInfo.RedirectStandardOutput = true;
CMD.StartInfo.WorkingDirectory = @"C:\";
CMD.Start();
StreamWriter CMDInput = CMD.StandardInput;
StreamReader CMDOutput = CMD.StandardOutput;
string InputString = Console.ReadLine();
CMDInput.WriteLine(InputString);
string OutputString = CMDOutput.ReadToEnd();
Console.WriteLine(OutputString);
经过多年的搜索和试验后,我只是不确定从这里该何去何从。
解决方案[柠檬天空]:
感谢 Lemon Sky 快速而简单的回复。对于遇到我的问题的任何其他人,可以在下面找到我用来解决它的代码。显然,我需要的确切事件已经存在一个事件侦听器,我所要做的就是像 Lemon Sky 所说的那样订阅它 (Process.OutputDataReceived
)。如果没有任何过滤,您仍然会收到包含您的命令的行,因此作为一个简单的修复,我添加了一些过滤来检测正在输出的行是否以 C:\
.
Process CMD = new Process();
CMD.StartInfo.FileName = "cmd.exe";
CMD.StartInfo.Arguments = "/K";
CMD.StartInfo.UseShellExecute = false;
CMD.StartInfo.RedirectStandardInput = true;
CMD.StartInfo.RedirectStandardOutput = true;
CMD.StartInfo.WorkingDirectory = @"C:\";
CMD.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
if (e.Data.StartsWith(@"C:\"))
{
return;
}
else
{
Console.WriteLine(e.Data);
}
}
});
CMD.Start();
CMD.BeginOutputReadLine();
StreamWriter CMDInput = CMD.StandardInput;
while (true && !CMD.HasExited)
{
string InputString = Console.ReadLine();
if (InputString == "cls" || InputString == "clear")
Console.Clear();
else
CMDInput.WriteLine(InputString);
}
再次感谢柠檬天空!
设置process.StartInfo.RedirectStandardOutput = true
,订阅process.OutputDataReceived
,调用process.BeginOutputReadLine
。这将适用于由换行符分隔的基于文本的输出。