I receive console output in my application. I use code from here (the accepted solution). But I never get null in my OutputDataReceived. Instead, I have String.Empty at the end of the output. Would it be correct to use String.NullOrEmpty instead of just comparing to null?
static void Main(string[] args)
{
var command = @"wmic cpu get loadpercentage";
using (Process process = new Process())
{
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardError = true;
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
process.OutputDataReceived += (sndr, a) =>
{
if (a.Data == null)
{
outputWaitHandle.Set();
}
else
{
output.AppendLine(a.Data);
}
};
process.ErrorDataReceived += (sndr, a) =>
{
if (a.Data == null)
{
errorWaitHandle.Set();
}
else
{
error.AppendLine(a.Data);
}
};
process.Start();
process.BeginOutputReadLine();
outputWaitHandle.WaitOne();
string path = "Test.txt";
using (StreamWriter sw = File.Exists(path) ? File.AppendText(path) : File.CreateText(path))
{
sw.WriteLine(String.Format("{0}, {1}", DateTime.Now, output));
}
}
}
}
Update: It seems that it won't work multiline output. The question is why there's no null in a.Data
The standard output stream is only terminated with the process itself is terminated. Note that here, "process" means the process running cmd.exe
. That process can start other processes, but they have their own standard output stream, and while those process's stdout will be terminated when they themselves terminate, none of that affects the parent cmd.exe
process's stdout.
If you want to run a process that runs various other processes by emulating user input to cmd.exe
, then you will also have to emulate the user interaction that would recognize and respond to the final command being executed.
Imagine yourself in a real user's place and think about how they deal with the situation. When they execute a command in a command-prompt window, does that window close when the command has completed? No. Do they receive any sort of indication that the command has completed? Sort of. First, they are presumably expected the output of the command to conform to some specific format, which often will include some indicator that the command has completed. Second, the command-prompt window will display a new prompt (i.e. the "prompt" in the phrase "command-prompt").
Note that a malicious command could figure out what the current prompt looks like and fake it. But that's unusual and presumably you have enough control over the commands you're issuing to avoid that. So one approach is to simply process the output as it occurs and detect the new prompt that appears when the command has completed.
If that doesn't seem reliable enough, then you will have to handle each command individually, interpreting the output from the command and recognizing when that command has reached the end of its output solely from the content in that output.
Finally note that you can in fact use the Process
class to execute the commands themselves. You don't need cmd.exe
to do that. And if you execute each command as a separate process yourself, then you do wind up getting notification of the termination of the process via the end-of-stream of the stdout for each process.