C# Process Live StandardOutput Stream

529 Views Asked by At

I am trying to call rclone from my C# project using Process, and also needs to get the live output from the process.

To achieve this, I am using the below code, found below:

It was taken from 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, 0, 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, 0, 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();
        }


    }
}

and I am calling this using this:

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();

However, the problem I'm having is that the first byte of the stream, (the T from Transferring:... message) is getting output separately from the rest of the stream.

The loghandler is just a method I wrote to write to a WPF richtextbox on the UI thread. writeToLog(string message); just appends a line to RTB, writeLogToBuffer(); writes the log to a buffer that can be loaded to load the RTB contents back with loadLogFromBuffer();.

So, what is the problem here? Is this a rclone quirk? How do I go about solving this? Thanks in advance!

1

There are 1 best solutions below

1
Philipp Ape On

You could use the EnableRaisingEvents property in the Process. Set it to true and call the BeginOutputReadLine() method to start for output listening. Then you are able to use the OutputDataReceived event. Whenever data comes in, this event will be triggered.