C# – Read console output asynchronously

There are a lot of open source softwares which can be invoked through console command. If we want to integrate these programs into our software, we must handle how to input the arguments such as get the result back. If you read my blog post C# – MP3/WAV Converter with Lame/NAudio before, you’ll see how I call lame – a console application – in my application. However I didn’t explain much about how to read its output. So in this post, I’ll explain in details how we can read the output of cmd.exe.

1. Ping command’s result

I will use a template of MVVM Pattern for WPF to generate a WPF application to host the console. This WPF application simply consists of one text box, one button and one text block to simulate the Ping command of Windows OS. The TextBox contains a website URL which we would like to ping. The TextBlock displays the result of Ping command.

Console Program

The “Ping It!” button will be binded with the DownloadCommand

public ICommand DownloadCommand
{
	get
	{
		if (downloadCommand == null)
			downloadCommand = new RelayCommand(() => PingLink());
		return downloadCommand;
	}

}

This DownloadCommand will call DownLoadRTMP function in which instantiate an instance of class RTMPDump which provides a Download function to ping to the website.

private void PingLink()
{
	OutPutCmd = "";
	cmdDump = new CMDDump();
	cmdDump.OutPutCmdChangedEvent += new EventHandler(cmdDump_OutPutCmdChangedEvent);
	cmdDump.Download(DownloadLink);    
}

In the Download function of CMDDump, I will locate where is cmd.exe then run it with the input of Pingcommand

public void Download(string link)
{
	cmdProc.StartInfo.FileName = Environment.GetEnvironmentVariable("COMSPEC"); 
	cmdProc.StartInfo.CreateNoWindow = true;
        cmdProc.StartInfo.UseShellExecute = false;
	cmdProc.StartInfo.RedirectStandardInput = true;
	cmdProc.StartInfo.RedirectStandardOutput = true;
	cmdProc.OutputDataReceived += new DataReceivedEventHandler(myProc_OutputDataReceived);	

	cmdProc.Start();
	cmdProc.BeginOutputReadLine();

	System.IO.StreamWriter myWriter;
	myWriter = cmdProc.StandardInput;
	myWriter.AutoFlush = true;
	
	myWriter.WriteLine("ping.exe " + link);	
	myWriter.Close();

        waitThread = new Thread(new ThreadStart(WaitForProcess));
        waitThread.Start();
}

void myProc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{            
	OutPutCmdChangedEvent(this, new MyEventArgs(e.Data));
}

private void WaitForProcess()
{
	cmdProc.WaitForExit();
	cmdProc.Close();
}

As you can see, I redirect the input of cmd.exe from Standard.Input (keyboard) to my StreamWriter through setting the property RedirectStandardInput to true. Remember to set UseShellExecute to false if you want to set RedirectStandardInput to true. Otherwise, writing to the StandardInput stream throws an exception. When redirecting the input to StreamWriter I also specify the input of a process, for example in this case I specify the input as Ping command.

After redirecting the input, I would like to redirect the output to my TextBlock. The redirected StandardOutput stream can be read synchronously or asynchronously. You can set methods of process such as Read, ReadLine, and ReadToEnd to perform synchronous read operations on the output stream of the process. These synchronous read operations do not complete until the associated Process writes to its StandardOutput stream, or closes the stream. I don’t want to use these synchronous methods because I must wait until the Ping command finishes its work and receive complete result back. I would like to to write immediately what is going on the console screen to my TextBlock therefore I use BeginOutputReadLine function.

Not like synchronous functions, BeginOutputReadLine starts asynchronous read operations on the StandardOutput stream. This method enables a designated event handler for the stream output and immediately returns to the caller, which can perform other work while the stream output is directed to the event handler. In this event handler, I give the result back to MainViewModel through an event again. That keeps GUI update with the content of console.

Please note that if we process the asynchronous output, we should call the WaitForExit() method to ensure that the output buffer has been flushed. Therefore I made another thread to wait for Ping command and close the console when Ping is finished. Do not call WaitForExit() in the same thread of console process, otherwise it will block your GUI from updating from event OutPutCmdChangedEvent.

2. Conclusions

That’s all about how to read the console output. Maybe it’s slightly different for reading output from other apps. For example rtmpdump like following

rtmpdump.exe -r rtmp://gffstream.fcod.llnwd.net/a792/e2/tv/rockpalast/live/2010/haldern10_feine_kleine_dorfmusik.mp4 -o output.mp4

because 3rd program doesn’t send message out correctly. However the whole idea works, you may need some modifications.

Source code : Read console output asynchronously

UPDATE 19.08.2011
The problem of rtmpdump can be solved now. The code above works. I couldn’t see the output before because rtmpdump sometimes sends information into error channel instead of output channel and I stupidly caught only the output channel in my code.

3 thoughts on “C# – Read console output asynchronously”

Leave a Reply

Your email address will not be published. Required fields are marked *