Thursday, April 19, 2012

C# - Using the Command Prompt

Summary
This is a C# tutorial on how to interact with the command line programatically.

Description
Even now, the command prompt is used by many programs.  We can easily integrate the command line into our application.  If you can open the Command Prompt and execute a command, you can have your application do the same thing. 

In this tutorial we will explain how to use the command line inside our application.  We will create a command-line class that can be used in any scenario (Object Oriented Programming).  The Cmd.cs class is located at the end of the tutorial. 

Before we begin
I'm using Visual Studio 2010 on Windows 7 x64. 

According to Microsoft:

"Command Prompt is a feature of Windows that provides an entry point for typing MS‑DOS (Microsoft Disk Operating System) commands and other computer commands. The most important thing to know is that by typing commands, you can perform tasks on your computer without using the Windows graphical interface. Command Prompt is typically only used by advanced users."

Learn more by reading about the Command Prompt.

Steps
Command Prompt
  1. Setup a ProcessStartInfo object (Instance of the ProcessStartInfo class).
  2. Create a new Process and assign the ProcessStartInfo to it.
  3. Write commands to the process by using a StreamWriter.
  4. Read the output and errors (if any)
  5. Close the process
  6. Extra - The whole tutorial written as a handy static class.


Begin
1. Setup a ProcessStartInfo object (Instance of the ProcessStartInfo class).

We are creating a new ProcessStartInfo object (Figure 1.1).

 // Create an instance of the ProcessStartInfo class
ProcessStartInfo info = new ProcessStartInfo();
// Redirect input,output, and error
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;

// This must be set to false
info.UseShellExecute = false;
// This will open the command prompt
info.FileName = "cmd.exe";
// We don't want to create a window
info.CreateNoWindow = true;

Figure 1.1


2. Create a new Process and assign the ProcessStartInfo to it.

Next, we need to make the Process, give it the ProcessStartInfo, and start the Process (Figure 2.1)

// Make a new Process
 Process proc = new Process();
// Set the Process object's start info to the above StartProcessInfo
proc.StartInfo = info;
// Start the process
proc.Start(); 
Figure 2.1


3. Write commands to the process by using a StreamWriter.

Now that we have started the process we are going to write something to it.  In this case, the process we started was, "cmd.exe" and we will write commands to it using an instance of the StreamWriter (Figure 3.1).

In this case, we are writing two commands.  The first is "cd C:\windows\microsoft.net\" and it only changes the directory (cd) to "C:\windows\microsoft.net\".  The second command is to exit.
// The stream writer is replacing the keyboard as the input
// and we are tell the stream writer what to write
using (StreamWriter writer = proc.StandardInput)
{
 // If the streamwriter is able to write
 if (writer.BaseStream.CanWrite)
 {
  // Write the command that was passed into the method
  writer.WriteLine(@"cd C:\windows\microsoft.net\");
  // Exit the command window
  writer.WriteLine("exit");
 }
 // close the StreamWriter
 writer.Close();
}
Figure 3.1



4.  Read the output and errors (if any)

Now that we have said what we wanted to in the command prompt.  We should display the output and errors (if any) from the process (Figure 4.1). 

 // Get the output from the command line
string output = proc.StandardOutput.ReadToEnd();
// Get any Error's that may exist
string error = proc.StandardError.ReadToEnd();

// Write the output
Console.Out.Write("This is the output: " + output);
Console.Out.Write(Environment.NewLine);
Console.Out.Write("This is the error: " + error);

Figure 4.1


 5. Close the process

This is it! Close the process (Figure 5.1).



When we start debugging, we see the following output (Figure 5.2)







6. Extra - The whole tutorial written as a handy static class.

Below is the whole tutorial written as a class called Cmd.cs and has two static methods named Run.  One method accepts a command as a string and the other accepts multiple commands as a string array.  A string array is returned from both methods.  The error is always given in the first element of the return string array.  The output is returned as the second element of the return string array when the output is set to true.

Hope it helps


public static class Cmd
    {
        public static string[] Run(string command, bool output)
        {
            /*
             *  New array of two strings.
             *  string[0] is the error message.
             *  string[1] is the output message.
             */
            string[] message = new string[2];

            // ProcessStartInfo allows better control over
            // the soon to executed process
            ProcessStartInfo info = new ProcessStartInfo();

            // Input to the process is going to come from the Streamwriter
            info.RedirectStandardInput = true;

            // Output from the process is going to be put into message[1]
            info.RedirectStandardOutput = true;

            // Error, if any, from the process is going to be put into message[0]
            info.RedirectStandardError = true;

            // This must be set to false
            info.UseShellExecute = false;

            // We want to open the command line
            info.FileName = "cmd.exe";

            // We don't want to see a command line window
            info.CreateNoWindow = true;

            // Instantiate a Process object
            Process proc = new Process();



            // Set the Process object's start info to the above StartProcessInfo
            proc.StartInfo = info;

            // Start the process
            proc.Start();



            // The stream writer is replacing the keyboard as the input
            using (StreamWriter writer = proc.StandardInput)
            {
                // If the streamwriter is able to write
                if (writer.BaseStream.CanWrite)
                {
                    // Write the command that was passed into the method
                    writer.WriteLine(command);
                    // Exit the command window
                    writer.WriteLine("exit");
                }
                // close the StreamWriter
                writer.Close();
            }

            // Get any Error's that may exist
            message[0] = proc.StandardError.ReadToEnd();

            // If the output flag was set to true
            if (output)
            {
                // Get the output from the command line
                message[1] = proc.StandardOutput.ReadToEnd();
            }

            // close the process
            proc.Close();

            // return the any error/output
            return message;
        }

        public static string[] Run(string[] command, bool output)
        {
            string[] message = new string[2];

            ProcessStartInfo info = new ProcessStartInfo();

            info.RedirectStandardInput = true;
            info.RedirectStandardOutput = true;
            info.RedirectStandardError = true;

            info.UseShellExecute = false;
            info.FileName = "cmd.exe";
            info.CreateNoWindow = true;

            Process proc = new Process();
            proc.StartInfo = info;
            proc.Start();

            using (StreamWriter writer = proc.StandardInput)
            {
                if (writer.BaseStream.CanWrite)
                {
                    foreach (string q in command)
                    {
                        writer.WriteLine(q);
                    }
                    writer.WriteLine("exit");
                }
            }

            message[0] = proc.StandardError.ReadToEnd();

            if (output)
            {
                message[1] = proc.StandardOutput.ReadToEnd();
            }

            return message;
        }



       

    }






2 comments:

  1. This is great! Thanks!

    ReplyDelete
  2. proc.Close(); is missing from the multi-command method.

    My use case is an application which returns data via StandardError however with CreateNoWindow = true nothing is returned (but it does produce the expected output with CreateNoWindow = false). Do you have any suggestions please?

    With CreateNoWindow = true command "dir non_existant_file" produces the expected error in StandardError.

    ReplyDelete