C# 处理实时标准输出流

问题描述

我正在尝试使用 Process 从我的 C# 项目中调用 rclone,并且还需要从过程中获取实时输出

为了实现这一点,我使用了以下代码,如下所示:

取自this post

using System;
using System.IO;

namespace Collection_Manager
{
    /// <summary>
    /// Stream reader for StandardOutput and StandardError stream readers
    /// Runs an eternal BeginRead loop on the underlaying stream bypassing the stream reader.
    /// 
    /// The TextReceived sends data received on the stream in non delimited chunks. Event subscriber can
    /// then split on newline characters etc as desired.
    /// </summary>
    class AsyncStreamReader
    {

        public delegate void EventHandler<args>(object sender,string Data);
        public event EventHandler<string> DataReceived;

        protected readonly byte[] buffer = new byte[4096];
        private StreamReader reader;


        /// <summary>
        ///  If AsyncStreamReader is active
        /// </summary>
        public bool Active { get; private set; }


        public void Start()
        {
            if (!Active)
            {
                Active = true;
                BeginReadAsync();
            }
        }


        public void Stop()
        {
            Active = false;
        }


        public AsyncStreamReader(StreamReader readerToBypass)
        {
            reader = readerToBypass;
            Active = false;
        }


        protected void BeginReadAsync()
        {
            if (Active)
            {
                reader.BaseStream.BeginRead(buffer,buffer.Length,new AsyncCallback(ReadCallback),null);
            }
        }

        private void ReadCallback(IAsyncResult asyncResult)
        {
            int bytesRead = reader.BaseStream.EndRead(asyncResult);

            string data = null;

            //Terminate async processing if callback has no bytes
            if (bytesRead > 0)
            {
                data = reader.CurrentEncoding.GetString(buffer,bytesRead);
            }
            else
            {
                //callback without data - stop async
                Active = false;
            }

            //Send data to event subscriber - null if no longer active
            if (DataReceived != null)
            {
                DataReceived.Invoke(this,data);
            }

            //Wait for more data from stream
            BeginReadAsync();
        }


    }
}

我用这个来调用它:

logHandler.writeLogToBuffer();

uploadProcess.StartInfo.UseShellExecute = false;
uploadProcess.StartInfo.CreateNowindow = true;            uploadProcess.StartInfo.RedirectStandardOutput = true;
uploadProcess.StartInfo.FileName = "rclone.exe";
uploadProcess.StartInfo.StandardOutputEncoding = Encoding.UTF8;
uploadProcess.StartInfo.Arguments =
                "sync -P " +
                "\"" + sourceDirectory + "\" " +
                "\"" + Properties.Settings.Default.remoteName + "/" + destination + "\" " +
                "--exclude '.*{/**,}' " +
                "--config \"" + Properties.Settings.Default.rcloneConfig + "\" " +
                Properties.Settings.Default.rcloneArgs;

uploadProcess.Start();

AsyncStreamReader stdOut = new AsyncStreamReader(uploadProcess.StandardOutput);
stdOut.DataReceived += (sender,data) =>
{
    if (!string.IsNullOrEmpty(data))
    {
        if (data.Contains("ETA"))
        {
            logHandler.loadLogFromBuffer();
        }
        logHandler.writetoLog(data);
    }
};
stdOut.Start();

但是,我遇到的问题是流的第一个字节(来自 T 消息的 Transferring:...)与流的其余部分分开输出

日志处理程序只是我写的一种方法,用于在 UI 线程上写入 WPF 富文本框。 writetoLog(string message); 只是向 RTB 追加一行,writeLogToBuffer(); 将日志写入缓冲区,该缓冲区可以通过 loadLogFromBuffer(); 加载回 RTB 内容

那么,这里的问题是什么?这是 rclone 的怪癖吗?我该如何解决这个问题? 提前致谢!

解决方法

您可以在流程中使用 EnableRaisingEvents 属性。将其设置为 true 并调用 BeginOutputReadLine() 方法以开始输出侦听。 然后您就可以使用 OutputDataReceived 事件。 每当有数据进来,就会触发这个事件。