【问题标题】:Redirecting standard input of console application重定向控制台应用程序的标准输入
【发布时间】:2014-03-17 21:09:50
【问题描述】:

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

我用重定向的标准输入流创建了进程。代码sn-p如下,

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

但我可以应付。

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

【问题讨论】:

  • 循环中的代码是什么样的?
  • @Watson 我没有控制台应用程序的代码。它是第三方工具。
  • 请不要在问题标题中包含有关所用语言的信息,除非没有它就没有意义。标记用于此目的。

标签: c# process iostream io-redirection


【解决方案1】:

这是我为了处理这类事情而编写的一个类。随意使用它。它的目的是启动一个控制台应用程序并与之“对话”。它也具有接收输出的能力。祝你好运。

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

【讨论】:

  • 感谢代码。由于我使用的是 VS2010,我无法使用异步和等待。 :( 我在编辑和删除所有异步部分后尝试了代码(不确定它是否正确),但我也遇到了我在问题中用编辑后的代码解释过的同样问题..
  • 您确定答案必须包含如此多的噪音,例如区域和明显的 xml cmets?
  • 很抱歉,它是从 stylecop 格式化的文件中复制粘贴的。
  • 我尝试通过var cam = new ConsoleAppManager("sudo"); cam.ExecuteAsync("apt install vim"); while (cam.Running) Thread.Sleep(1000); 使用它,但它不起作用(它挂起),我错过了什么吗?
  • @neo 好的,我会添加3.5版本,可能明天有时间。
【解决方案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();
    }
}

【讨论】:

    【解决方案3】:

    感谢 ConsoleAppManager,它运行良好,但我不得不提一下,使用 Process 类:OutputDataReceivedErrorDataReceived 中的事件并调用 @ 可以实现相同的捕获输出和错误消息的效果987654324@ 和BeginErrorReadLine() 启动进程后。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-27
      • 2019-03-15
      • 1970-01-01
      • 2010-11-06
      相关资源
      最近更新 更多