Click here to Skip to main content
15,886,816 members
Articles / Programming Languages / C#

CommandLineHelper class to launch console applications and capture their output

Rate me:
Please Sign up or sign in to vote.
4.66/5 (12 votes)
23 Oct 20068 min read 73.4K   604   64  
Runs a console application and returns its output as a string. Useful for writing object-oriented wrappers around command line utilities, such as Subversion's svn utility.
using System;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Security.Permissions;

namespace AndrewTweddle.Tools.Utilities.CommandLine
{
    [SecurityPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
    public static class CommandLineHelper
    {
        private delegate string StringDelegate();

        public static string Run(string fileName, string arguments, 
            out string errorMessage)
        {
            errorMessage = "";
            Process cmdLineProcess = new Process();
            using (cmdLineProcess)
            {
                cmdLineProcess.StartInfo.FileName = fileName;
                cmdLineProcess.StartInfo.Arguments = arguments;
                cmdLineProcess.StartInfo.UseShellExecute = false;
                cmdLineProcess.StartInfo.CreateNoWindow = true;
                cmdLineProcess.StartInfo.RedirectStandardOutput = true;
                cmdLineProcess.StartInfo.RedirectStandardError = true;

                if (cmdLineProcess.Start())
                {
                    return ReadProcessOutput(cmdLineProcess, ref errorMessage, 
                        fileName);
                }
                else
                {
                    throw new CommandLineException(String.Format(
                        "Could not start command line process: {0}", 
                        fileName));
                    /* Note: arguments aren't also shown in the 
                     * exception as they might contain privileged 
                     * information (such as passwords).
                     */
                }
            }
        }

        private static string ReadProcessOutput(Process cmdLineProcess, 
            ref string errorMessage, string fileName)
        {
            StringDelegate outputStreamAsyncReader
                = new StringDelegate(cmdLineProcess.StandardOutput.ReadToEnd);
            StringDelegate errorStreamAsyncReader
                = new StringDelegate(cmdLineProcess.StandardError.ReadToEnd);

            IAsyncResult outAR 
                = outputStreamAsyncReader.BeginInvoke(null, null);
            IAsyncResult errAR = errorStreamAsyncReader.BeginInvoke(null, null);

            if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
            {
                /* WaitHandle.WaitAll fails on single-threaded 
                 * apartments. Poll for completion instead:
                 */
                while (!(outAR.IsCompleted && errAR.IsCompleted))
                {
                    /* Check again every 10 milliseconds: */
                    Thread.Sleep(10);
                }
            }
            else
            {
                WaitHandle[] arWaitHandles = new WaitHandle[2];
                arWaitHandles[0] = outAR.AsyncWaitHandle;
                arWaitHandles[1] = errAR.AsyncWaitHandle;

                if (!WaitHandle.WaitAll(arWaitHandles))
                {
                    throw new CommandLineException(
                        String.Format("Command line aborted: {0}", fileName));
                    /* Note: arguments aren't also shown in the 
                     * exception as they might contain privileged 
                     * information (such as passwords).
                     */
                }
            }

            string results = outputStreamAsyncReader.EndInvoke(outAR);
            errorMessage = errorStreamAsyncReader.EndInvoke(errAR);

            /* At this point the process should surely have exited,
             * since both the error and output streams have been fully read.
             * To be paranoid, let's check anyway...
             */
            if (!cmdLineProcess.HasExited)
            {
                cmdLineProcess.WaitForExit();
            }

            return results;
        }

        public static string Run(string fileName, string arguments)
        {
            string result;
            string errorMsg = String.Empty;
            
            result = Run(fileName, arguments, out errorMsg);

            if (errorMsg.Length > 0)
                throw new CommandLineException(errorMsg);

            return result;
        }

        public static string Run(string fileName)
        {
            return Run(fileName, "");
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Dariel Solutions
South Africa South Africa
Andrew Tweddle started his career as an Operations Researcher, but made the switch to programming in 1997. His current programming passions are Powershell and WPF.

He has worked for one of the "big 4" banks in South Africa as a software team lead and an architect, at a Dynamics CRM consultancy and is currently an architect at Dariel Solutions working on software for a leading private hospital network.

Before that he spent 7 years at SQR Software in Pietermaritzburg, where he was responsible for the resource planning and budgeting module in CanePro, their flagship product for the sugar industry.

He enjoys writing utilities to streamline the software development and deployment process. He believes Powershell is a killer app for doing this.

Andrew is a board game geek (see www.boardgamegeek.com) with a collection of over 190 games! He also enjoys digital photography, camping and solving puzzles - especially Mathematics problems.

His Myers-Briggs personality profile is INTJ.

He lives with his wife, Claire and his daughters Lauren and Catherine in Johannesburg, South Africa.

Comments and Discussions