17

Summary:

  • query registry on remote machine
  • capture output to use in application
  • needs to be in csharp
  • so far all methods used can only query on the local machine
  • any hope is greatly appreciated

Full issue:

I need to find a way to run a commandline command in csharp and capture its output. I know how to do this in Perl, below is the code I would use in Perl.

#machine to check
my $pc = $_[0];
#create location of registry query
my $machine = "\\\\".$pc."\\HKEY_USERS";
#run registry query
my @regQuery= `REG QUERY $machine`;

Any suggestions on how to do this in csharp would be welcome. So far ive tried using the RegistryKey OurKey = Registry.Users method and it works great but i can not query the registry on a remote machine.

Please let me know if you need any more information.

SOLUTION:(Thank you to @Robaticus)

private void reg(string host)
        {

            string build = "QUERY \\\\" + host + "\\HKEY_USERS";
            string parms = @build;
            string output = "";
            string error = string.Empty;

            ProcessStartInfo psi = new ProcessStartInfo("reg.exe", parms);

            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
            psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
            psi.UseShellExecute = false;
            System.Diagnostics.Process reg;
            reg = System.Diagnostics.Process.Start(psi);
            using (System.IO.StreamReader myOutput = reg.StandardOutput)
            {
                output = myOutput.ReadToEnd();
            }
            using (System.IO.StreamReader myError = reg.StandardError)
            {
                error = myError.ReadToEnd();

            }
            Output.AppendText(output + "\n");


        }  
toosweetnitemare
  • 2,226
  • 8
  • 33
  • 44

6 Answers6

30

You might have to tweak this a bit, but here's some (slightly modified from the original) code that redirects stdout and stderr for a process:

        string parms = @"QUERY \\machine\HKEY_USERS";
        string output = "";
        string error = string.Empty;

        ProcessStartInfo psi = new ProcessStartInfo("reg.exe", parms);

        psi.RedirectStandardOutput = true;
        psi.RedirectStandardError = true;
        psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
        psi.UseShellExecute = false;
        System.Diagnostics.Process reg;
        reg = System.Diagnostics.Process.Start(psi);
        using (System.IO.StreamReader myOutput = reg.StandardOutput)
        {
            output = myOutput.ReadToEnd();
        }
        using(System.IO.StreamReader myError = reg.StandardError)
        {
            error = myError.ReadToEnd();

        }
Robaticus
  • 22,857
  • 5
  • 54
  • 63
  • Thank you for the code. I just tried to implement and execute it. Im having issues with grabbing the output from it.currently im using Output.AppendText(output + "\n"); to print the output with. is this correct? Im new to csharp (about 3 hrs of experience total :) ) – toosweetnitemare Jan 03 '11 at 19:37
  • This is my solution. I just had to actually toss the machine name into the variable :). Thank you very much! – toosweetnitemare Jan 03 '11 at 19:42
  • 2
    Note that this code *should* work for `reg.exe`, but will hang with a deadlock for a program that writes enough to its standard error stream to fill up the default buffer size. The correct solution for the general case is to read both output streams at the same time, with separate threads. – Daniel Pryden Jan 03 '11 at 23:31
8

Practically anything you can run in the command line you can run in a C# program with similar constraints. There's a few ways to do it, one is via Asynchronous process commands as I show in my blog. You just write and read to the command line in an active fashion. From here, just figure out what you want to accomplish and how to do it with a command line. Then plug it into the program

class Program
{
static void Main(string[] args)
{
LaunchCommandAsProcess cmd = new LaunchCommandAsProcess();
cmd.OutputReceived += new LaunchCommandAsProcess.OutputEventHandler(launch_OutputReceived);
cmd.SendCommand("help");
cmd.SendCommand("ipconfig");
cmd.SyncClose();
}
/// Outputs normal and error output from the command prompt.
static void launch_OutputReceived(object sendingProcess, EventArgsForCommand e)
{
Console.WriteLine(e.OutputData);
}
}

As you can see, you simply instantiate the class, handle the output event, and start writing commands just like you were typeing into the command prompt.

Here’s how it works:

public class LaunchCommandAsProcess
{
public delegate void OutputEventHandler(object sendingProcess, EventArgsForCommand e);
public event OutputEventHandler OutputReceived;
private StreamWriter stdIn;
private Process p;
public void SendCommand(string command)
{
stdIn.WriteLine(command);
}
public LaunchCommandAsProcess()
{
p = new Process();
p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();

stdIn = p.StandardInput;
p.OutputDataReceived += Process_OutputDataReceived;
p.ErrorDataReceived += Process_OutputDataReceived;
p.BeginOutputReadLine();
p.BeginErrorReadLine();

}
///
/// Raises events when output data has been received. Includes normal and error output.
/// 

/// /// private void Process_OutputDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine.Data == null)
return;
else
{
if (OutputReceived != null)
{
EventArgsForCommand e = new EventArgsForCommand();
e.OutputData = outLine.Data;
OutputReceived(this, e);
}
}
}
///
/// Synchronously closes the command promp.
/// 

public void SyncClose()
{
stdIn.WriteLine("exit");
p.WaitForExit();
p.Close();
}
///
/// Asynchronously closees the command prompt.
/// 

public void AsyncClose()
{
stdIn.WriteLine("exit");
p.Close();
}
}
public class EventArgsForCommand : EventArgs
{
public string OutputData { get; internal set; }
}
P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
5

Here's a class I use. It's adapted from code I found in a blog posting a while ago, but with various other modifications.

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace SonomaTechnologyInc {
    /// <summary>
    /// Utility class for working with command-line programs.
    /// </summary>
    public class Subprocess {  
        private Subprocess() { }

        /// <summary>
        /// Executes a command-line program, specifying a maximum time to wait
        /// for it to complete.
        /// </summary>
        /// <param name="command">
        /// The path to the program executable.
        /// </param>
        /// <param name="args">
        /// The command-line arguments for the program.
        /// </param>
        /// <param name="timeout">
        /// The maximum time to wait for the subprocess to complete, in milliseconds.
        /// </param>
        /// <returns>
        /// A <see cref="SubprocessResult"/> containing the results of
        /// running the program.
        /// </returns>
        public static SubprocessResult RunProgram(string command, string args, int timeout) {
            bool timedOut = false;
            ProcessStartInfo pinfo = new ProcessStartInfo(command);
            pinfo.Arguments = args;
            pinfo.UseShellExecute = false;
            pinfo.CreateNoWindow = true;
            //pinfo.WorkingDirectory = ?
            pinfo.RedirectStandardOutput = true;
            pinfo.RedirectStandardError = true;
            Process subprocess = Process.Start(pinfo);

            ProcessStream processStream = new ProcessStream();
            try {
                processStream.Read(subprocess);

                subprocess.WaitForExit(timeout);
                processStream.Stop();
                if(!subprocess.HasExited) {
                    // OK, we waited until the timeout but it still didn't exit; just kill the process now
                    timedOut = true;
                    try {
                        subprocess.Kill();
                        processStream.Stop();
                    } catch { }
                    subprocess.WaitForExit();
                }
            } catch(Exception ex) {
                subprocess.Kill();
                processStream.Stop();
                throw ex;
            } finally {
                processStream.Stop();
            }

            TimeSpan duration = subprocess.ExitTime - subprocess.StartTime;
            float executionTime = (float) duration.TotalSeconds;
            SubprocessResult result = new SubprocessResult(
                executionTime, 
                processStream.StandardOutput.Trim(), 
                processStream.StandardError.Trim(), 
                subprocess.ExitCode, 
                timedOut);
            return result;
        }
    }

    /// <summary>
    /// Represents the result of executing a command-line program.
    /// </summary>
    public class SubprocessResult {
        readonly float executionTime;
        readonly string stdout;
        readonly string stderr;
        readonly int exitCode;
        readonly bool timedOut;

        internal SubprocessResult(float executionTime, string stdout, string stderr, int exitCode, bool timedOut) {
            this.executionTime = executionTime;
            this.stdout = stdout;
            this.stderr = stderr;
            this.exitCode = exitCode;
            this.timedOut = timedOut;
        }

        /// <summary>
        /// Gets the total wall time that the subprocess took, in seconds.
        /// </summary>
        public float ExecutionTime {
            get { return executionTime; }
        }

        /// <summary>
        /// Gets the output that the subprocess wrote to its standard output stream.
        /// </summary>
        public string Stdout {
            get { return stdout; }
        }

        /// <summary>
        /// Gets the output that the subprocess wrote to its standard error stream.
        /// </summary>
        public string Stderr {
            get { return stderr; }
        }

        /// <summary>
        /// Gets the subprocess's exit code.
        /// </summary>
        public int ExitCode {
            get { return exitCode; }
        }

        /// <summary>
        /// Gets a flag indicating whether the subprocess was aborted because it
        /// timed out.
        /// </summary>
        public bool TimedOut {
            get { return timedOut; }
        }
    }

    internal class ProcessStream {
        /*
         * Class to get process stdout/stderr streams
         * Author: SeemabK (seemabk@yahoo.com)
         * Usage:
            //create ProcessStream
            ProcessStream myProcessStream = new ProcessStream();
            //create and populate Process as needed
            Process myProcess = new Process();
            myProcess.StartInfo.FileName = "myexec.exe";
            myProcess.StartInfo.Arguments = "-myargs";

            //redirect stdout and/or stderr
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.RedirectStandardOutput = true;
            myProcess.StartInfo.RedirectStandardError = true;

            //start Process
            myProcess.Start();
            //connect to ProcessStream
            myProcessStream.Read(ref myProcess);
            //wait for Process to end
            myProcess.WaitForExit();

            //get the captured output :)
            string output = myProcessStream.StandardOutput;
            string error = myProcessStream.StandardError;
         */
        private Thread StandardOutputReader;
        private Thread StandardErrorReader;
        private Process RunProcess;
        private string _StandardOutput = "";
        private string _StandardError = "";

        public string StandardOutput {
            get { return _StandardOutput; }
        }
        public string StandardError {
            get { return _StandardError; }
        }

        public ProcessStream() {
            Init();
        }

        public void Read(Process process) {
            try {
                Init();
                RunProcess = process;

                if(RunProcess.StartInfo.RedirectStandardOutput) {
                    StandardOutputReader = new Thread(new ThreadStart(ReadStandardOutput));
                    StandardOutputReader.Start();
                }
                if(RunProcess.StartInfo.RedirectStandardError) {
                    StandardErrorReader = new Thread(new ThreadStart(ReadStandardError));
                    StandardErrorReader.Start();
                }

                int TIMEOUT = 1 * 60 * 1000; // one minute
                if(StandardOutputReader != null)
                    StandardOutputReader.Join(TIMEOUT);
                if(StandardErrorReader != null)
                    StandardErrorReader.Join(TIMEOUT);

            } catch { }
        }

        private void ReadStandardOutput() {
            if(RunProcess == null) return;
            try {
                StringBuilder sb = new StringBuilder();
                string line = null;
                while((line = RunProcess.StandardOutput.ReadLine()) != null) {
                    sb.Append(line);
                    sb.Append(Environment.NewLine);
                }
                _StandardOutput = sb.ToString();
            } catch { }
        }

        private void ReadStandardError() {
            if(RunProcess == null) return;
            try {
                StringBuilder sb = new StringBuilder();
                string line = null;
                while((line = RunProcess.StandardError.ReadLine()) != null) {
                    sb.Append(line);
                    sb.Append(Environment.NewLine);
                }
                _StandardError = sb.ToString();
            } catch { }
        }

        private void Init() {
            _StandardError = "";
            _StandardOutput = "";
            RunProcess = null;
            Stop();
        }

        public void Stop() {
            try { if(StandardOutputReader != null) StandardOutputReader.Abort(); } catch { }
            try { if(StandardErrorReader != null) StandardErrorReader.Abort(); } catch { }
            StandardOutputReader = null;
            StandardErrorReader = null;
        }
    }
}
Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • 2
    I adapted your code and created a class library that [I've put up on GitHub](https://github.com/kenny-evitt/ExecuteCommandLineProgram). Any problem with that? – Kenny Evitt Aug 16 '13 at 16:55
  • 1
    @KennyEvitt: No problem from me. As far as I know the code is mine (except for the parts that came from commenter "SeemabK" on Scott Hanselman's blog, of course). I no longer work for the employer I was working for when I wrote that, but I don't believe they have any claim to it either. So I think you're fine. – Daniel Pryden Aug 17 '13 at 05:44
3

This doesn't answer the question, but the Registry.OpenRemoteBaseKey method connects to another machine's registry in the same way that the REG command does. Call RegistryKey.GetSubKeyNames to obtain the same output as REG QUERY.

Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
0

You can capture StandardOutput and StandardError using the System.Diagnostics.Process class.

http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx

Be sure to read the remarks section of the documentation. Certain properties of the process class have to be set correctly for StandardOutput to be available (e.g. UseShellExecute must be set to false).

mwilson
  • 1,255
  • 12
  • 17
0

As I mentioned in this comment on this answer, I adapted the code in that answer and created a library:

From the library README:

Example usage:

CommandLineProgramProcessResult result =
    CommandLineProgramProcess.RunProgram(
        @"C:\Program Files (x86)\SomeFolder\SomeProgram.exe",             // Path of executable program
        @"C:\Program Files (x86)\SomeFolder\",                            // Path of working directory
        String.Format(@"""{0}""", filePathThatNeedsToBeQuotedArgument),   // Command line arguments
        10 * 60 * 1000);                                                  // Timeout, in milliseconds
        
string standardError = result.StandardError;
string standardOutput = result.StandardOutput;
int exitCode = result.ExitCode;

All of the library code is in this file:

Basically, the (sole) library class uses .NET streams to both write to the command process's input stream and read both its output and error streams. The output and error streams are read using separate threads (i.e. in parallel) to avoid "a deadlock for a program that writes enough to its standard error stream to fill up the default buffer size".

Kenny Evitt
  • 9,291
  • 5
  • 65
  • 93