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