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
Related
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!
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.
I have a process that I need hidden, I have tried the following lines of code to make it hidden:
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.CreateNoWindow = true;
The first line just simply does not make it non-visible and the second throws the following error:
"{"StandardOut has not been redirected or the process hasn't started yet."}
Also I need to have the output redirected to a richtextbox and the clipboard, so I cannot set redirectstandardoutput to false.
Here is my function for creating the process.
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = pingData;
p.Start();
p.WaitForExit();
string result = p.StandardOutput.ReadToEnd();
System.Windows.Forms.Clipboard.SetText(result);
if(p.HasExited)
{
richTextBox1.Text = result;
outPut = result;
MessageBox.Show( "Ping request has completed. \n Results have been copied to the clipboard.");
}
Thanks
Remove the following line:
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Keep these two lines:
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
WindowStyle only applies to native Windows GUI applications.
I do have a strange problem: I try to call git.exe to clone a repository via a small c# console-application->
Process p = new Process();
p.StartInfo.FileName = "path/to/git/git.exe";
p.StartInfo.Arguments = "clone SomeGitRepoWhichRequiresPassword";
p.StartInfo.UseShellExecute = false;
p.EnableRaisingEvents = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += OutputDataReceived;
p.ErrorDataReceived += ErrorDataReceived;
p.Exited += Exited;
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
When I try to clone a Repository which needs user-input (e.g. entering a ssh-password) none of the events is triggered so I cannot react to that input programmaticly. Is there a way to really get ALL command-output?
Thanks!
Why do you want to enter password programatically? You can try to write password in Git repository URL like this:
git clone https://username:password#github.com/username/repository.git
This is windows-application-form code; I want the batch file which is going to be executed to show the output on shell screen which I got by RedirectStandardOutput = false;, but I also want output to be redirected to a log file at the same time. For this, I use RedirectStandardOutput = true;.
Of course, only one can be used at one time!
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "c:\test\build.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true; // if I use false all the commented lines below are not applicable for comments
p.Start();
string output = null;
//try
//{
output = p.StandardOutput.ReadToEnd();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.ToString());
//}
System.IO.File.WriteAllText("c:\test\log.txt", output);
Capture the output and print it to the screen yourself. That's how the tee command meets this need on most non-Windows operating systems.
You could try something like this:
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "c:\test\build.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
p.Start();
And have an event handler somewhere in your code:
private static void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
Console.WriteLine(outLine.Data);
System.IO.File.AppendAllText("c:\test\log.txt", outLine.Data);
}
}
Example taken from MSDN: Process.BeginOutputReadLine Method. It would be more efficient to keep the file open for writing, or even to buffer it but this keeps the example short.