重定向控制台应用程序的标准输入

时间:2014-02-18 08:42:31

标签: c# process iostream io-redirection

我有一个控制台应用程序,我试图通过重定向该过程的标准输入流来自动化。在打开应用程序后的手动模式下,它等待用户输入,如下所示enter image description here

我使用重定向的标准输入流创建了该过程。代码段如下,

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 image description here

就像我正在向进程输入流连续发送Enter键命令。谁能指出我在这里做错了什么?

同样,标准输出流重定向在制作

后也无法正常工作
  

newProcess.StartInfo.RedirectStandardOutput = true

但我可以解决这个问题。

标准流的重定向是否适用于所有控制台应用程序,或者是否有例外?

3 个答案:

答案 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}} 并在启动过程后调用 ErrorDataReceivedBeginOutputReadLine()

此外,BeginErrorReadLine() 正在杀死 CPU,可能希望在 while 循环和 WriteInputTask() 的设置之间使用某种 ResetEvent 或将输入直接发送到进程。