I'm trying to execute command prompt commands and read the output in C#. This is my code:
ProcessStartInfo cmdInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
cmdInfo.CreateNoWindow = true;
cmdInfo.RedirectStandardOutput = true;
cmdInfo.UseShellExecute = false;
Process cmd = new Process();
cmd.StartInfo = cmdInfo;
cmd.Start();
string result = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
cmd.Close();
return result;
It works most of the time, but sometimes result="" when that's impossible for the command I'm using (for example, route add should give output on success or failure). Any ideas? I was wondering if maybe I'd created a race condition between the process and the ReadToEnd call?
Not all output is written to StandardOutput; many applications will instead write to StandardError if something goes wrong. You would have to read from both to get all of the output.
As long as the application never blocks for input, it should be safe to call ReadToEnd() on both output streams to get all of the output. A safer option, however, is to hook up an event to the OutputDataReceived and ErrorDataReceived events. You can attach lambda expression to these that close over local variables to make things pretty simple:
var output = new StringBuilder();
var error = new StringBuilder();
cmd.OutputDataReceived += (o, e) => output.Append(e.Data);
cmd.ErrorDataReceived += (o, e) => error.Append(e.Data);
cmd.Start();
cmd.BeginOutputReadLine();
cmd.BeginErrorReadLine();
cmd.WaitForExit();
Related
I used this code to log the output from the exe in command prompt to RIchTextBox.
ProcessStartInfo psi = new ProcessStartInfo("adb.exe", "devices");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;
var proc = Process.Start(psi);
string s = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
richTextBox1.Text = s;
This is basically android command to get the list of connected devices. This works fines as it has just two lines.
If the output of the exe is continous data how it can be logged efficiently.
I replaced the command with adb.exe logcat but it hangs and nothing comes on the RichTextBox.
How can I log this coninous output on the RichetxtBox?
It's because you call ReadToEnd on the output stream; C# will keep reading it and reading it and only finish reading it when the stream closes. At that point your code carries on. Your task is more complex than you realize. To read incrementally you need something like:
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Properties.Settings.Default.CommandLineFfmpegPath,
Arguments = string.Format(
Properties.Settings.Default.CommandLineFfmpegArgs,
OutputPath
),
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true
};
var proc = System.Diagnostics.Process.Start(psi);
proc.OutputDataReceived += proc_OutputDataReceived;
proc.ErrorDataReceived += proc_ErrorDataReceived;
proc.BeginOutputReadLine();
And you need an event handler that will be called every time there is some data (but it will need to make sure it doesn't cause a cross thread violation), something like:
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (richTextbox1.InvokeRequired)
richTextbox1.Invoke((MethodInvoker) delegate {this.Text += e.Data;});
else
richTextbox1.Text += e.Data;
}
I'm not sure I'd use something as heavy as a richtextbox for this.. You probably aren't formatting (unless the console output is colored and youre gonna reinterpret the color codes) so a textbox would do fine.
Oh, and you probably don't want to jam your UI thread by WaitForExit either
I made the following simple example which should capture all the output and send it to my server where it is just decoded back to string (C# server) and wrote to console using Console.WriteLine():
var process = new Process();
process.StartInfo.FileName = "cmd";
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = "/C " + command;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (s, e) => {
if(e.Data == null)
return;
var bytes = Encoding.Default.GetBytes(e.Data);
client.Send(bytes, bytes.Length);
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
To test this stuff, as command I used systeminfo which displays what is currently doing on the first line (probably using \r) and then displays all the data over multiple lines. I tested the systeminfo in CMD. The event is trigerred just when all data appears, which means some seconds (when just the first console line is written) I get nothing. Basically, I don't have any way to see on the server what's happening.
How can I capture literally everything? I also managed to use process.ErrorDataReceived with its redirect activated. I didn't got anymore the lines on the client console (the right lines appeared in the console, but the event wasn't triggered), but the trigger is still not called. What I did wrong?
EDIT (due possible duplicate):
I changed the code in order to make use of StandardOutput:
process.Start();
Task.Run(() => {
var buf = new char[256];
int readed;
while(! process.HasExited) {
readed = process.StandardOutput.Read(buf, 0, 256);
var bytes = Encoding.Default.GetBytes(buf, 0, readed);
client.Send(bytes, bytes.Length);
}
});
The same behavior happens, the code reaches the bytes declaration within while when the command is writing normal lines, but while it is writing on the first line, nothing happens. Tested it with StandardError too.
EDIT (what's happening on the server):
while(true)
Console.WriteLine(Encoding.Default.GetString(server.Receive(ref client)));
In everything above I set breakpoints on the .Send() so I know what the client is sending.
I try execute systeminfo cmd commnand and I have a problem. My problem is that when it comes to method WaitForExit(), the program doesn't continue. I try
using (var cmd = new Process())
{
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine("systeminfo");
cmd.StandardInput.Flush();
cmd.WaitForExit();
var result = cmd.StandardOutput.ReadToEnd();
cmd.StandardInput.Close();
cmd.StandardOutput.Close();
return result;
}
Is there a particular reason you go through cmd here? That's so utterly unnecessary here and also explains your problem. You run an interactive instance of cmd, pretending as if you typed systeminfo and then wait for cmd to exit. Just as a normal interactive instance, it won't do that. It will just sit there, waiting for more commands. Instead, why not just call systeminfo directly?
using (var systeminfo= new Process
{
FileName = "systeminfo",
RedirectStandardOutput = true,
CreateNoWindow = false,
UseShellExecute = false,
})
{
systeminfo.Start();
var result = systeminfo.StandardOutput.ReadToEnd();
systeminfo.WaitForExit();
return result;
}
You can try the following options:
Option 1:
Subscribe to Process.Exited event and when the systeminfo process exits, read the stream to get the result of the process.
Option 2:
Redirect the systeminfo process output to a text file. When process exists event occurs, read the result from the text file.
also instead of cmd.StandardInput.WriteLine("systeminfo"); pass the systeminfo process as the argument. It will provide you more control.
ex: cmd.StartInfo.Arguments = "/C systeminfo.exe";
You must add command to cmd, cmd run that and return results and exit.
Use command like "cmd /c ver" works fine and return windows version.
I'm converting some c# code to java
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
// Display the command output.
This is the code that i try to convert it to java.
I dont know how to run a command and use process in java. I googled it and i found something like that :
Process process = Runtime.getRuntime().exec(command);
Integer result = process.exitValue();
In the line
process.exitValue()
it gives me java.lang.IllegalThreadStateException: process has not exited.
After exec you need to wait for the command to finish with process.waitFor().
process.exitValue() gets the number from System.exit(number)
The error is thrown since the process hasn't exited yet.
From the c# code it looks like you want to get the command's output, this can be done by:
int i=0;
String result = new String();
while((i = process.getInputStream().read()) >= 0)
result += (char)i;
System.out.println(result);
I am working on an application that calls several command line applications to do some post processing on some video files.
Right now I am trying to use Comskip to identify the commercial breaks in a video recording from my cable card tuner. This runs just fine, but I am having problems getting the screen output that I need.
String stdout = null;
using (var process = new Process())
{
var start = new ProcessStartInfo(comskip, cmdLine);
start.WindowStyle = ProcessWindowStyle.Normal;
start.CreateNoWindow = true;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
process.StartInfo = start;
process.Start();
process.WaitForExit();
stdout = process.StandardOutput.ReadToEnd();
}
I'm expecting stdout to grab what is displayed on the screen the same as when the application is launched manually (screen shot below) which is a continuous feed of what the application is doing, and mixed in the output are lines that give a % progress, which I want to use to update a progress bar
But running the above code only gives me:
The commandline used was:
"C:\Users\Chris\Google Drive\Tools\ComSkip\comskip.exe" "C:\Users\Chris\Desktop\ComSkip Tuning Files\Modern Family.wtv" "--ini=C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini"
Setting ini file to C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini as per commandline
Using C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini for initiation values.
I also tried redirecting the StandardError stream and grabbing process.StandardError.ReadToEnd(); but the process appears to hang if I run with these options.
Am I missing something to capture what I'm hoping for, or is it possible that the output stream for this application is going somewhere else that is not accessible?
You must set following:
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += new DataReceivedEventHandler(ReadOutput);
process.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutput);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
and catch the output in ReadOutput and ErrorOutput
private static void ErrorOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
stdout = "Error: " + e.Data;
}
}
private static void ReadOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
stdout = e.Data;
}
}
See the docs on RedirectStandardOutput. Waiting for the child process to end before reading the output can cause a hang.
It particular, the example says not to do what you have done:
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
You should use the events OutputDataReceived and possibly ErrorDataReceived and update the progress bar in the handler.