I am having trouble capturing the output from Powershell version 2.
I use the regular Process class to run the program and handle input and output and everything works great. Working code below:
using (Process p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "powershell";
//p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.OutputDataReceived += (sender, args) => AddRow(args.Data);
p.ErrorDataReceived += (sender, args) => AddRow("ERR: " + args.Data);
p.Start();
ps = p.StandardInput;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
Thread.Sleep(1000);
ps.WriteLine("dir");
Thread.Sleep(100);
while (true)
{
Thread.Sleep(10);
}
}
This version starts powershell and sends "dir" to stdinput of the process and the result is sent back to stdout. I can see the cmd-window but i cant write or see any output there as expected. (I know i can hide it).
But then i add this row:
p.StartInfo.Arguments = "-version 2";
This is to start powershell in version 2 and now it just prints 2 rows that tells me powershell is started and then the connection is lost.
I see in the console window that the cursor is blinking and now i can interact with powershell in the command window since the connection to my process class is lost. When i enter "exit" in the window i am back again to my process.
How can i solve this?
Full code (when using version 2 that is not working) below:
using (Process p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "powershell";
p.StartInfo.Arguments = "-version 2";
//p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.OutputDataReceived += (sender, args) => AddRow(args.Data);
p.ErrorDataReceived += (sender, args) => AddRow("ERR: " + args.Data);
p.Start();
ps = p.StandardInput;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
Thread.Sleep(1000);
ps.WriteLine("dir");
Thread.Sleep(100);
while (true)
{
Thread.Sleep(10);
}
}
I hope someone can help me with this! Thanks for your time!
Related
Why do I NOT get 3 lines of output from running TestApp remotely? In string 'output' I get license info plus "Start#" line, BUT not the next two lines:
public static string RunPsExec()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = #"C:\Users\Vence\Downloads\PSTools\PsExec.exe";
p.StartInfo.Arguments = #"\\10.215.yy.yy -u xxxxxx -p ""xxxxxx"" C:\Projects\TestApp\TestApp\bin\Debug\TestApp.exe";
p.Start();
string output = p.StandardOutput.ReadToEnd();
string errormessage = p.StandardError.ReadToEnd();
p.WaitForExit();
return output;
}
TestApp:
static void Main(string[] args)
{
Console.WriteLine("Start# " + DateTime.Now.ToShortTimeString());
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Middle# " + DateTime.Now.ToShortTimeString());
System.Threading.Thread.Sleep(5000);
Console.WriteLine("End# " + DateTime.Now.ToShortTimeString());
}
One solution that I see is set RedirectStandardInput to false, since it is not needed in this example.
However, if you would like to pass the input to the TestApp in a future (which requires RedirectStandardInput=true), one solution might be to use PAexec (https://www.poweradmin.com/paexec/) as an alternative to PsExec. According to these question (Can't receive asynchronous output from psexec when launching application remotely), PAExec writes directly to console buffer, instead of stout/sterr. To make it work, you would have to read the output asynchronously, like this:
public static string RunPsExec()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = #"C:\<path_to>\PaExec.exe";
p.StartInfo.Arguments = #\\10.215.yy.yy -u xxxxxx -p ""xxxxxx"" ""C:\Projects\TestApp\TestApp\bin\Debug\TestApp.exe"" ";
p.OutputDataReceived += (sender, args) => Display(args.Data);
p.Start();
p.BeginOutputReadLine();
p.WaitForExit(10000);
return;
}
static void Display(string output)
{
Console.WriteLine(output);
}
This question already has answers here:
Capturing console output from a .NET application (C#)
(8 answers)
Closed 6 years ago.
I need to spawn a child process that is a console application, and capture its output.
I wrote up the following code for a method:
string retMessage = String.Empty;
ProcessStartInfo startInfo = new ProcessStartInfo();
Process p = new Process();
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = command;
startInfo.FileName = exec;
p.StartInfo = startInfo;
p.Start();
p.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
using (StreamReader output = p.StandardOutput)
{
retMessage = output.ReadToEnd();
}
}
);
p.WaitForExit();
return retMessage;
However, this does not return anything. I don't believe the OutputDataReceived event is being called back, or the WaitForExit() command may be blocking the thread so it will never callback.
Any advice?
EDIT: Looks like I was trying too hard with the callback. Doing:
return p.StandardOutput.ReadToEnd();
Appears to work fine.
Here's code that I've verified to work. I use it for spawning MSBuild and listening to its output:
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.Start();
process.BeginOutputReadLine();
I just tried this very thing and the following worked for me:
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "<insert command line arguments here>";
processStartInfo.FileName = "<insert tool path here>";
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
// use the output
string output = outputBuilder.ToString();
Here's some full and simple code to do this. This worked fine when I used it.
var processStartInfo = new ProcessStartInfo
{
FileName = #"C:\SomeProgram",
Arguments = "Arguments",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(processStartInfo);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Note that this only captures standard output; it doesn't capture standard error. If you want both, use this technique for each stream.
I needed to capture both stdout and stderr and have it timeout if the process didn't exit when expected. I came up with this:
Process process = new Process();
StringBuilder outputStringBuilder = new StringBuilder();
try
{
process.StartInfo.FileName = exeFileName;
process.StartInfo.WorkingDirectory = args.ExeDirectory;
process.StartInfo.Arguments = args;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.EnableRaisingEvents = false;
process.OutputDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.ErrorDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var processExited = process.WaitForExit(PROCESS_TIMEOUT);
if (processExited == false) // we timed out...
{
process.Kill();
throw new Exception("ERROR: Process took too long to finish");
}
else if (process.ExitCode != 0)
{
var output = outputStringBuilder.ToString();
var prefixMessage = "";
throw new Exception("Process exited with non-zero exit code of: " + process.ExitCode + Environment.NewLine +
"Output from process: " + outputStringBuilder.ToString());
}
}
finally
{
process.Close();
}
I am piping the stdout and stderr into the same string, but you could keep it separate if needed. It uses events, so it should handle them as they come (I believe). I have run this successfully, and will be volume testing it soon.
It looks like two of your lines are out of order. You start the process before setting up an event handler to capture the output. It's possible the process is just finishing before the event handler is added.
Switch the lines like so.
p.OutputDataReceived += ...
p.Start();
Redirecting the stream is asynchronous and will potentially continue after the process has terminated. It is mentioned by Umar to cancel after process termination process.CancelOutputRead(). However that has data loss potential.
This is working reliably for me:
process.WaitForExit(...);
...
while (process.StandardOutput.EndOfStream == false)
{
Thread.Sleep(100);
}
I didn't try this approach but I like the suggestion from Sly:
if (process.WaitForExit(timeout))
{
process.WaitForExit();
}
You need to call p.Start() to actually run the process after you set the StartInfo. As it is, your function is probably hanging on the WaitForExit() call because the process was never actually started.
The answer from Judah did not work for me (or is not complete) as the application was exiting after the first BeginOutputReadLine();
This works for me as a complete snippet, reading the constant output of a ping:
var process = new Process();
process.StartInfo.FileName = "ping";
process.StartInfo.Arguments = "google.com -t";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += (sender, a) => Console.WriteLine(a.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
Here's a method that I use to run a process and gets its output and errors :
public static string ShellExecute(this string path, string command, TextWriter writer, params string[] arguments)
{
using (var process = Process.Start(new ProcessStartInfo { WorkingDirectory = path, FileName = command, Arguments = string.Join(" ", arguments), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }))
{
using (process.StandardOutput)
{
writer.WriteLine(process.StandardOutput.ReadToEnd());
}
using (process.StandardError)
{
writer.WriteLine(process.StandardError.ReadToEnd());
}
}
return path;
}
For example :
#"E:\Temp\MyWorkingDirectory".ShellExecute(#"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe", Console.Out);
I'm looking to trigger the child command window's close event once its command is finished. Keep in mind, it's a background process initiated from a console app so it's never visible. What is visible is the console application.
I tried using the Exited event, but that didn't work. I tried relying on CMD to know when to close it by using /c, /k, and exit. Neither seem to work. I also tried a do while loop checking HasExited, none of these have worked unless I type "exit" within the application console window. It does not close, but somehow triggers the invisible child command windows to close.
Is there another way of closing it once the child command is complete?
String msg = "echo %time%; exit;";
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = msg;
p.EnableRaisingEvents = true;
p.Exited += p_Exited;
p.Start();
msg += p.StandardOutput.ReadToEnd();
Thank you very much!!
I modified your program slightly to run a child command processor, capture its output, then write it to console.
char quote = '"';
string msg = "/C " + quote + "echo %time%" + quote;
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = msg;
p.EnableRaisingEvents = true;
p.Exited += (_, __) => Console.WriteLine("Exited!");
p.Start();
string msg1 = p.StandardOutput.ReadToEnd();
Console.WriteLine(msg1);
Here's a full program, using slightly different syntax, but similar in spirit:
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
char quote = '"';
var startInfo = new ProcessStartInfo("cmd", "/C " + quote + "echo %time%" + quote)
{ UseShellExecute = false, RedirectStandardOutput = true };
var process = new Process { EnableRaisingEvents = true };
process.StartInfo = startInfo;
process.Exited += (_, __) => Console.WriteLine("Exited!");
process.Start();
string msg1 = process.StandardOutput.ReadToEnd();
Console.WriteLine(msg1);
Console.ReadLine();
}
}
}
Or, as this answer illustrates, maybe just call DateTimeOffset.Now. If you're interested in looking at sub-second info, maybe use Stopwatch class instead.
If you prefer to drive command line with commands from C#, it's also possible. Igor Ostrovsky describes how to convert events to Tasks; then use async/await to create a procedural-looking sequence of commands and responses.
Is there anyway to redirect standard output of a spawned process and capture it as its happening. Everything I have seen just does a ReadToEnd after the process has finished. I would like to be able to get the output as it is being printed.
Edit:
private void ConvertToMPEG()
{
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
//Setup filename and arguments
p.StartInfo.Arguments = String.Format("-y -i \"{0}\" -target ntsc-dvd -sameq -s 720x480 \"{1}\"", tempDir + "out.avi", tempDir + "out.mpg");
p.StartInfo.FileName = "ffmpeg.exe";
//Handle data received
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.Start();
}
void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Debug.WriteLine(e.Data);
}
Use Process.OutputDataReceived event from the process, to recieve the data you need.
Example:
var myProc= new Process();
...
myProc.StartInfo.RedirectStandardOutput = true;
myProc.OutputDataReceived += new DataReceivedEventHandler(MyProcOutputHandler);
...
private static void MyProcOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
....
}
}
So after a little more digging I found out that ffmpeg uses stderr for output. Here is my modified code to get the output.
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.Arguments = String.Format("-y -i \"{0}\" -target ntsc-dvd -sameq -s 720x480 \"{1}\"", tempDir + "out.avi", tempDir + "out.mpg");
p.StartInfo.FileName = "ffmpeg.exe";
p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.Start();
p.BeginErrorReadLine();
p.WaitForExit();
I'm learning C# at the moment for a bit of fun and am trying to make a windows application that has a bit of a gui for running some python commands. Basically, I'm trying to teach myself the guts of running a process and sending commands to it, as well as receiving commands from it.
I have the following code at the moment:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "C:/Python31/python.exe";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
textBox1.Text = output;
Running python.exe from a command prompt gives some introductory text that I'd like to capture and send to a textbox in the windows form (textBox1). Basically, the goal is to have something that looks like the python console running from the windows app. When I don't set UseShellExecute to false, a console pops up and everything runs fine; however, when I set UseShellExecute to false in order to re-direct the input, all I get is that a console pops up very quickly and closes again.
What am I doing wrong here?
For some reason, you shouldn't use forward slashes when you start the process.
Compare (does not work):
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = "C:/windows/system32/cmd.exe";
p.StartInfo.Arguments = "/c dir" ;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
bool f = p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
[...]
static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
to (works as expected):
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = #"C:\windows\system32\cmd.exe";
p.StartInfo.Arguments = "/c dir" ;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
bool f = p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
[...]
static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
Python seems to be doing something weird. I wouldn't believe it until I tested it and then did some research. But all of these posts basically seem to have the same exact problem:
https://stackoverflow.com/questions/4106095/capturing-standard-output-from-django-using-c
Python & C#: Is IronPython absolutely necessary?
C# capturing python.exe output and displaying it in textbox
Certain Python commands aren't caught in Stdout