C# - Realtime console output redirection

后端 未结 5 1269
眼角桃花
眼角桃花 2020-11-27 05:17

I\'m developing a C# application and I need to start an external console program to perform some tasks (extract files). What I need to do is to redirect the output

5条回答
  •  面向向阳花
    2020-11-27 05:51

    The struggle is over

    Thanks to the above samples I was able to solve the problems with the StandardOutput and StandardError stream readers blocking and impossible to use directly.

    MS admits here about locking problems: system.io.stream.beginread

    Subscribibng to StandardOutput and StandardError events using process.BeginOutputReadLine() and process.BeginErrorReadLine() and subscribibg to OutputDataReceived and ErrorDataReceived works fine but I miss out on newline characters and can not emulate what is happening on the original console being listened to.

    This class takes a reference to the StreamReader but captures console output from the StreamReader.BaseStream. The DataReceived event will deliver stream data forever as it arrives. Not blocking when tested on foreign console app.

        /// 
        /// 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.
        /// 
        class AsyncStreamReader
        { 
    
            public delegate void EventHandler(object sender, string Data);
            public event EventHandler DataReceived;
    
            protected readonly byte[] buffer = new byte[4096];
            private StreamReader reader;
    
    
            /// 
            ///  If AsyncStreamReader is active
            /// 
            public bool Active { get; private set; }
    
    
            public void Start()
            {
                if (!Active)
                {
                    Active = true;
                    BeginReadAsync();
                }           
            }
    
    
            public void Stop()
            {
                Active=false;         
            }
    
    
            public AsyncStreamReader(StreamReader readerToBypass)
            {
                this.reader = readerToBypass;
                this.Active = false;
            }
    
    
            protected void BeginReadAsync()
            {
                if (this.Active)
                {
                    reader.BaseStream.BeginRead(this.buffer, 0, this.buffer.Length, new AsyncCallback(ReadCallback), null);
                }
            }
    
            private void ReadCallback(IAsyncResult asyncResult)
            {
                var bytesRead = reader.BaseStream.EndRead(asyncResult);
    
                string data = null;
    
                //Terminate async processing if callback has no bytes
                if (bytesRead > 0)
                {
                    data = reader.CurrentEncoding.GetString(this.buffer, 0, bytesRead);
                }
                else
                {
                    //callback without data - stop async
                    this.Active = false;                
                }
    
                //Send data to event subscriber - null if no longer active
                if (this.DataReceived != null)
                {
                    this.DataReceived.Invoke(this, data);
                }
    
                //Wait for more data from stream
                this.BeginReadAsync();
            }
    
    
        }
    

    Maybe an explicit event when the AsyncCallback is exiting instead of sending a null string would be nice but basic problem was solved.

    The 4096 size buffer could be smaller. The callback will just loop until all data deliviered.

    Use like this:

                    standardOutput = new AsyncStreamReader(process.StandardOutput);
                    standardError = new AsyncStreamReader(process.StandardError);
    
                    standardOutput.DataReceived += (sender, data) =>
                    {
                        //Code here
                    };
    
                    standardError.DataReceived += (sender, data) =>
                    {
                        //Code here
                    };
    
    
                    StandardOutput.Start();
                    StandardError.Start();
    

提交回复
热议问题