1

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!

  • Try the code in this post: https://stackoverflow.com/questions/68208059/can-i-suppress-the-cr-crlf-sequence-in-a-cmd-exe-error-message/68215903#68215903 . Look at "Helper.cs (Complete code)" – Tu deschizi eu inchid Jul 03 '21 at 14:46
  • The post you linked me also uses `OutputDataReceived`, which still only applies for newlines. So, the whole output is not treated as a chunk, but will be read line by line async. – Min Gu Kang Jul 03 '21 at 17:07

1 Answers1

0

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.

Philipp Ape
  • 519
  • 3
  • 13
  • The reason for not using `BeginOutputReadLine();` is because it only allows me to read line by line. Since the whole output stream from the command line is required, this is unfortunately not applicable to my situation. Also, `EnableRaisingEvents` only enables `Process.Exited` function, which, also, does not help in getting a live output. – Min Gu Kang Jul 03 '21 at 15:31