我有一个控制台应用程序,我试图通过重定向该过程的标准输入流来自动化。在打开应用程序后的手动模式下,它等待用户输入,如下所示
我使用重定向的标准输入流创建了该过程。代码段如下,
Process newProcess = new Process();
newProcess.StartInfo.FileName = exeName;
newProcess.StartInfo.Arguments = argsLine;
newProcess.StartInfo.UseShellExecute = false;
newProcess.StartInfo.RedirectStandardOutput = false ;
newProcess.StartInfo.CreateNoWindow = false;
newProcess.StartInfo.RedirectStandardInput = true;
newProcess.Start();
但是创建这样的过程会产生如下所示的无限循环,
就像我正在向进程输入流连续发送Enter
键命令。谁能指出我在这里做错了什么?
同样,标准输出流重定向在制作
后也无法正常工作
newProcess.StartInfo.RedirectStandardOutput = true
但我可以解决这个问题。
标准流的重定向是否适用于所有控制台应用程序,或者是否有例外?
答案 0 :(得分:20)
这是我编写的一个类,用于处理这种东西。随意使用它。它的目的是启动一个控制台应用程序并与之“交谈”。它也能够接收输出。祝你好运。
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class ConsoleAppManager
{
private readonly string appName;
private readonly Process process = new Process();
private readonly object theLock = new object();
private SynchronizationContext context;
private string pendingWriteData;
public ConsoleAppManager(string appName)
{
this.appName = appName;
this.process.StartInfo.FileName = this.appName;
this.process.StartInfo.RedirectStandardError = true;
this.process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
this.process.StartInfo.RedirectStandardInput = true;
this.process.StartInfo.RedirectStandardOutput = true;
this.process.EnableRaisingEvents = true;
this.process.StartInfo.CreateNoWindow = true;
this.process.StartInfo.UseShellExecute = false;
this.process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
this.process.Exited += this.ProcessOnExited;
}
public event EventHandler<string> ErrorTextReceived;
public event EventHandler ProcessExited;
public event EventHandler<string> StandartTextReceived;
public int ExitCode
{
get { return this.process.ExitCode; }
}
public bool Running
{
get; private set;
}
public void ExecuteAsync(params string[] args)
{
if (this.Running)
{
throw new InvalidOperationException(
"Process is still Running. Please wait for the process to complete.");
}
string arguments = string.Join(" ", args);
this.process.StartInfo.Arguments = arguments;
this.context = SynchronizationContext.Current;
this.process.Start();
this.Running = true;
new Task(this.ReadOutputAsync).Start();
new Task(this.WriteInputTask).Start();
new Task(this.ReadOutputErrorAsync).Start();
}
public void Write(string data)
{
if (data == null)
{
return;
}
lock (this.theLock)
{
this.pendingWriteData = data;
}
}
public void WriteLine(string data)
{
this.Write(data + Environment.NewLine);
}
protected virtual void OnErrorTextReceived(string e)
{
EventHandler<string> handler = this.ErrorTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
protected virtual void OnProcessExited()
{
EventHandler handler = this.ProcessExited;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
protected virtual void OnStandartTextReceived(string e)
{
EventHandler<string> handler = this.StandartTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
private void ProcessOnExited(object sender, EventArgs eventArgs)
{
this.OnProcessExited();
}
private async void ReadOutputAsync()
{
var standart = new StringBuilder();
var buff = new char[1024];
int length;
while (this.process.HasExited == false)
{
standart.Clear();
length = await this.process.StandardOutput.ReadAsync(buff, 0, buff.Length);
standart.Append(buff.SubArray(0, length));
this.OnStandartTextReceived(standart.ToString());
Thread.Sleep(1);
}
this.Running = false;
}
private async void ReadOutputErrorAsync()
{
var sb = new StringBuilder();
do
{
sb.Clear();
var buff = new char[1024];
int length = await this.process.StandardError.ReadAsync(buff, 0, buff.Length);
sb.Append(buff.SubArray(0, length));
this.OnErrorTextReceived(sb.ToString());
Thread.Sleep(1);
}
while (this.process.HasExited == false);
}
private async void WriteInputTask()
{
while (this.process.HasExited == false)
{
Thread.Sleep(1);
if (this.pendingWriteData != null)
{
await this.process.StandardInput.WriteLineAsync(this.pendingWriteData);
await this.process.StandardInput.FlushAsync();
lock (this.theLock)
{
this.pendingWriteData = null;
}
}
}
}
}
答案 1 :(得分:2)
按照上一个答案,我只是添加SubArray的扩展方法,以防万一将此类添加到您的代码中,而不是嵌套在任何类中(代码在评论中看起来不可读,所以我在这里添加了它)
public static class CharArrayExtensions
{
public static char[] SubArray(this char[] input,int startIndex, int length)
{
List<char> result= new List<char>();
for (int i = startIndex; i < length; i++)
{
result.Add(input[i]);
}
return result.ToArray();
}
}
答案 2 :(得分:0)
感谢您提供 ConsoleAppManager,它运行良好,但我不得不提到,使用 Process
类中的事件可以实现捕获输出和错误消息的相同效果:OutputDataReceived
和 {{ 1}} 并在启动过程后调用 ErrorDataReceived
和 BeginOutputReadLine()
。
此外,BeginErrorReadLine()
正在杀死 CPU,可能希望在 while 循环和 WriteInputTask()
的设置之间使用某种 ResetEvent
或将输入直接发送到进程。>