I have worked on this issue for a while. I can capture the output(live) of the console window just fine, but I can't capture the output of a python console application in real time. I can capture the output of the python program after it has finished running, but i don't want that.
I am using process from system.diagonistics. with a background worker.
I simply want to capture the python26 output onto a text box. I have tested my program with other custom applications, and it does display the output(live).
Help please
Thanks
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace ProcessDisplayoutput
{
public partial class Form1 : Form
{
//Delegates
delegate void AppendTextDelegate(string text);
public Form1()
{
InitializeComponent();
Worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
}
private void StartButton_Click(object sender, EventArgs e)
{
ResultTextBox.Clear();
if (!Worker.IsBusy)
{
Worker.RunWorkerAsync();
}
}
public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Process pro = new Process();
pro.StartInfo.RedirectStandardOutput = true;
pro.StartInfo.RedirectStandardError = true;
pro.StartInfo.UseShellExecute = false;
pro.StartInfo.CreateNoWindow = true;
pro.EnableRaisingEvents = true;
pro.OutputDataReceived +=new DataReceivedEventHandler(OnDataReceived);
pro.ErrorDataReceived +=new DataReceivedEventHandler(OnDataReceived);
//Test with random program worked,
//now need to test with python
//*****************TEST 1: PASSED **************************
pro.StartInfo.FileName = "C:\\TestProcessOutput.exe";
//*****************END TEST1*******************************
//*****************TEST 2: FAILED *************************
//pro.StartInfo.FileName = "C:\\Python26\\python.exe";
//pro.StartInfo.Arguments = "\"C:\\Python26\\testScript.py\"";
//*****************END TEST2 *******************************
StreamReader sr = null;
try
{
pro.Start();
pro.BeginOutputReadLine();
//An alternative option to display the output with the same results
//sr = pro.StandardOutput;
//string line = "";
//while ((line = sr.ReadLine()) != null)
//{
// appendText(line);
// }
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public void OnDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string temp = (e.Data) + Environment.NewLine;
appendText(temp);
}
}
public void appendText(string text)
{
if (ResultTextBox.InvokeRequired)
{
ResultTextBox.Invoke(new AppendTextDelegate(appendText), new object[] { text });
}
else
{
ResultTextBox.AppendText(text);
}
}
I just ran into this question myself, and after a ton of experimenting, what worked for me was running the python process with the "-u" option, which makes the output unbuffered. With that, everything worked completely fine.
I ran into this problem while making a MiniConsole exactly for that purpose.
I used your technique with
pro.EnableRaisingEvents = true;
pro.OutputDataReceived +=new DataReceivedEventHandler(OnDataReceived);
pro.ErrorDataReceived +=new DataReceivedEventHandler(OnDataReceived);
The strange thing is that all the output was coming from ErrorDataReceived instead of OutputDataReceived (with valid commands).
So I think you're missing:
pro.BeginErrorReadLine();
Also I was starting the process in the main thread (I don't have any worker), using python27.
Here is the full start:
// executable: "c:\\python27\\python.exe", arguments: "myscript.py"
ProcessStartInfo startInfo = new ProcessStartInfo(executable, arguments);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.WorkingDirectory = textBoxWorkingDirectory.Text;
try
{
Process p = new Process();
p.StartInfo = startInfo;
p.EnableRaisingEvents = true;
p.OutputDataReceived += new DataReceivedEventHandler(OnDataReceived);
p.ErrorDataReceived += new DataReceivedEventHandler(OnDataReceived);
p.Exited += new EventHandler(OnProcessExit);
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
}
I remember having a similar issue a while back and I think I did something similar to this in my .py scripts instead of using the print function:
sLog = 'Hello World!'
subprocess.Popen( 'echo ' + sLog, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True )
Not sure if I set the shell parameter to True or False though. Also not sure about all the "std" parameters. You might want to experiment a bit there.
If you're starting the Python process from your code, then THIS will make your life really easy and I think it's about the cleanest way to go.
Related
I have little problem with stopping infinite Pinging.
If you see in picture I ping IP 127.0.0.1 it has infinite ping ( -t ).
And I want do that when I Click Stop! button then it stops pinging.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Net;
using System.Management;
namespace PingProgramm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Thread th;
private void button1_Click(object sender, EventArgs e)
{
th = new Thread(thread1);
th.Start();
}
public void thread1()
{
try
{
string command = "/c ping -t " + textBox1.Text;
ProcessStartInfo procStartInfo = new ProcessStartInfo("CMD", command);
Process proc = new Process();
proc.StartInfo = procStartInfo;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardInput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
catch (Exception)
{
//if an error occurs with in the try block, it will handled here.
}
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => richTextBox1.Text += newLine;
richTextBox1.BeginInvoke(append);
}
}
bool firstTime = true;
private void textBox1_Click(object sender, EventArgs e)
{
if (firstTime)
{
firstTime = false;
textBox1.Clear();
}
}
private void button2_Click(object sender, EventArgs e)
{
}
}
}
Best wishes
KLDesigns,
The simplest thing that might work in your current code is to get hold of your Process instance in the DataReceived event and cancel the process there (or send a Ctrl+C on the stdin).
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (stop)
{
// sender is our process instance
// so we can cast that safely here
var proc = (Process) sender;
// brutally kill it
proc.Kill();
// or more gently, send a ctrl+C
// http://stackoverflow.com/a/285041/578411
proc.StandardInput.Close();
}
if (e.Data != null)
{
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => richTextBox1.Text += newLine;
richTextBox1.BeginInvoke(append);
}
}
You can set the boolean stop in your click event handler:
bool stop = false;
private void button2_Click(object sender, EventArgs e)
{
stop = true;
}
Keep in mind that if you want to keep control over object instances you create consider keeping a reference to them. As you create the Process inside the thread your form can't reach it any more. If you would have made the proc an member of your form you could have called proc.StandardInput.Close() from your click event.
This question already has answers here:
How to read to end process output asynchronously in C#?
(4 answers)
Closed 8 years ago.
I have a textbox and a button. The button invokes Ping() and I want any progress of ping to be displayed in textBox1.
void Ping()
{
p = new Process();
p.StartInfo.FileName = "ping";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.Arguments = "www.microsoft.com";
p.Start();
textBox1.Text = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
How to achieve it? The current implementation I wrote above does not show the progress but the final output.
async void Test()
{
await Ping("www.google.com");
}
Task Ping(string host)
{
var tcs = new TaskCompletionSource<object>();
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "ping.exe";
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.Arguments = host;
var proc = Process.Start(psi);
proc.OutputDataReceived += (s, e) => {
Action action = () => textBox1.Text += e.Data + Environment.NewLine;
this.Invoke(action);
};
proc.Exited += (s, e) => tcs.SetResult(null);
proc.EnableRaisingEvents = true;
proc.BeginOutputReadLine();
return tcs.Task;
}
RESULT
Pinging www.google.com [64.15.117.154] with 32 bytes of data:
Reply from 64.15.117.154: bytes=32 time=50ms TTL=55
Reply from 64.15.117.154: bytes=32 time=45ms TTL=55
Reply from 64.15.117.154: bytes=32 time=45ms TTL=55
Reply from 64.15.117.154: bytes=32 time=46ms TTL=55
Ping statistics for 64.15.117.154:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 45ms, Maximum = 50ms, Average = 46ms
Having said that, I would use the Ping class instead of launching an external application
var ping = new System.Net.NetworkInformation.Ping();
var pingReply = await ping.SendPingAsync("www.google.com");
Console.WriteLine("{0} {1} {2}",pingReply.Address,
pingReply.RoundtripTime,
pingReply.Status);
Update
After receiving QtX comment, I went ahead and re-did the entire project in WinForms.
Here's the updated code and a screenshot, hope this helps you.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Process p;
public Form1()
{
InitializeComponent();
}
private void cmdPing_Click(object sender, EventArgs e)
{
p = new Process();
startNewThread();
}
public void Log(string line)
{
txtOutput.Text += line + System.Environment.NewLine;
}
private void startNewThread()
{
Thread x = new Thread(new ThreadStart(Ping));
x.IsBackground = true;
x.Start();
}
private void Ping()
{
p.StartInfo.FileName = "ping.exe";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.Arguments = txtAddress.Text;
p.Start();
while (p.StandardOutput.Peek() > -1)
{
this.Invoke((MethodInvoker)delegate() { Log(p.StandardOutput.ReadLine()); });
p.StandardOutput.DiscardBufferedData();
}
}
}
}
Screenshot of Form
I'm running a windows form with a background worker to update a textbox based on the output of a python script. Its all working pretty well, except the redirected output is not in real time; its delayed pretty significantly.
Any ideas how I can increase the redirected outputs response time?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace JiraHeartBeat
{
public partial class Form1 : Form
{
delegate void AppendTextDelegate(string text);
BackgroundWorker Worker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
Worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
}
void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StartButton.PerformClick();
}
private void StartButton_Click(object sender, EventArgs e)
{
if (!Worker.IsBusy)
{
Worker.RunWorkerAsync();
}
}
public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Process pro = new Process();
pro.StartInfo.RedirectStandardOutput = true;
pro.StartInfo.RedirectStandardError = true;
pro.StartInfo.UseShellExecute = false;
pro.StartInfo.CreateNoWindow = true;
pro.EnableRaisingEvents = true;
pro.OutputDataReceived += new DataReceivedEventHandler(OnDataReceived);
pro.ErrorDataReceived += new DataReceivedEventHandler(OnDataReceived);
pro.StartInfo.FileName = "C:\\Python27\\python.exe";
pro.StartInfo.Arguments = "\"C:\\Python27\\myscript.py\"";
try
{
pro.Start();
pro.BeginOutputReadLine();
pro.BeginErrorReadLine();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
Thread.Sleep(5000 * 60);
}
public void OnDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string temp = (e.Data) + Environment.NewLine;
appendText(temp);
}
}
public void appendText(string text)
{
if (ResultTextBox.InvokeRequired)
{
ResultTextBox.Invoke(new AppendTextDelegate(appendText), new object[] { text });
}
else
{
ResultTextBox.AppendText(text);
}
}
}
}
Actually, the issue is that Python does not redirect output until the script is complete. I believe IronPython will redirect while the script is running (have not tested this though), but unfortunately, regular Python must wait for the script to end before redirecting output.
Try removing the below line from the Worker_DoWork, I suspect it is delaying the execution of the RunWorkerCompleted event.
Thread.Sleep(5000 * 60);
EDIT
Since the above approach was attempted and did not solve the problem entirely I investigated a bit further and confirmed that when capturing the output from a python script the response is delayed. However, by adding a call to sys.stdout.flush() I was able to get the desired behavior. Here is the python script I used which worked successfully in my test.
import time
import sys
for x in xrange(0,11):
print x
time.sleep(1)
sys.stdout.flush()
Im trying to create a minecraft server wrapper but im having trouble reading the output of the process. I want to read the output in the serverProcess_ErrorDataRecevied event and I know that what there is now doesn't work. But what could I put there instead to read it?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
namespace MC_Server_UI
{
public partial class Form1 : Form
{
ProcessStartInfo startInfo = new ProcessStartInfo("C:\\Program Files (x86)\\Java\\jre7\\bin\\java.exe", "Xmx1024M - jar " + "craftbukkit.jar" + " nogui");
Process serverProcess;
OpenFileDialog ofd;
FolderBrowserDialog fbd;
public Form1()
{
InitializeComponent();
fbd = new FolderBrowserDialog();
fbd.ShowDialog();
startInfo.WorkingDirectory = fbd.SelectedPath;
startInfo.RedirectStandardInput = startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
serverProcess = new Process();
serverProcess.StartInfo = startInfo;
serverProcess.EnableRaisingEvents = true;
serverProcess.ErrorDataReceived += new DataReceivedEventHandler(serverProcess_ErrorDataReceived);
serverProcess.Exited += new EventHandler(serverProcess_Exited);
serverProcess.Start();
}
private void serverProcess_ErrorDataReceived(object sender, EventArgs e)
{
richTextBox1.AppendText(serverProcess.StandardError.ReadToEnd());
}
private void serverProcess_Exited(object sender, EventArgs e)
{
}
}
}
See:
How to spawn a process and capture its STDOUT in .NET?
ProcessStartInfo.RedirectStandardOutput
Code:
serverProcess.StartInfo.RedirectStandardOutput = true;
serverProcess.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
serverProcess.Start();
serverProcess.BeginOutputReadLine();
Or:
serverProcess.StartInfo.RedirectStandardOutput = true;
var output = serverProcess.StandardOutput.ReadToEnd();
See also: ProcessStartInfo.RedirectStandardError
serverProcess.StartInfo.RedirectStandardError = true;
var error = serverProcess.StandardError.ReadToEnd();
I'm writing an AIR app that launches a C# console application and they need to communicate. I'd like to use standard input/standard output for this, however I can't seem to get it to work.
When the C# app gets input from standard input it's supposed to send it back via standard output, and if it receives "exit" then it quits. I can test this from the command line and it works correctly. When I send a string from AIR, I get no response from the C# app.
I'm sending an argument when I launch the C# app, and I do get a response from that, so my AIR app is at least able to receive messages from standard output, it's just standard input that is not working. When I send a message from AIR via standardInput I get a progress event with bytesLoaded = 3 when I send the keycode, and bytesLoaded = 5 when I send the "exit" command.
Here is the C# code:
static void Main(string[] args)
{
if (args.Length > 0)
{
Console.WriteLine(args[0]);
}
while (true)
{
string incoming = Console.ReadLine();
string outgoing = "received: " + incoming;
Console.WriteLine(outgoing);
if (incoming == "exit")
return;
}
}
And here is the AS3 code:
private function init(e:Event=null):void {
this.removeEventListener(Event.ADDED_TO_STAGE, init);
NativeApplication.nativeApplication.addEventListener(Event.EXITING, onAppClose);
var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var file:File = File.applicationDirectory.resolvePath("test.exe");
info.executable = file;
process = new NativeProcess();
info.arguments.push("native process started");
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, onInputProgress);
process.addEventListener(Event.STANDARD_OUTPUT_CLOSE, onOutputClose);
process.addEventListener(Event.STANDARD_ERROR_CLOSE, onErrorClose);
process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
process.addEventListener(IOErrorEvent.STANDARD_INPUT_IO_ERROR, onIOError);
process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
process.start(info);
}
private function onKeyUp(e:KeyboardEvent):void {
if (e.keyCode == Keyboard.ESCAPE)
process.standardInput.writeUTFBytes("exit\n");
else {
var msg:String = e.keyCode + "\n";
process.standardInput.writeUTFBytes(msg);
}
}
private function onOutputData(e:ProgressEvent):void {
var data:String = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
trace("Got: ", data);
}
I encountered this issue a few months back but I never resolved it as I just used command line args instead. I have just returned to it though as I am keen to find out know what's going on.
I have now found that targeting .NET 3.5 or earlier makes it work as expected for me. Switch back to to v4.0 and I get nothing on stdin. I'm not exactly sure where the issue lies, but if you can get by with v3.5 then it might be a solution for you.
In case anyone else besides me still use Adobe AIR with C# console apps, here's a solution to the problem:
Just like #tom-makin points out in the linked answer, you need to create another console app that runs on .NET 3.5, which then opens your newer .NET console app and passes input to it. Here's my take on such an app:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace StdInPiper
{
class Program
{
static void Main(string[] args)
{
// Remove the first arg from args, containing the newer .NET exePath.
string exePath = args[0];
var tempArgs = new List<string>(args);
tempArgs.RemoveAt(0);
string argsLine = "";
foreach (string arg in tempArgs)
{
argsLine = argsLine + " " + arg;
}
argsLine = argsLine.Trim();
var process = new Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.Arguments = argsLine;
process.StartInfo.FileName = exePath;
process.OutputDataReceived += (sender, eventArgs) =>
{
Console.Write(eventArgs.Data);
};
process.ErrorDataReceived += (sender, eventArgs) =>
{
Console.Error.Write(eventArgs.Data);
};
process.Exited += (sender, eventArgs) =>
{
process.CancelOutputRead();
process.CancelErrorRead();
Environment.Exit(Environment.ExitCode);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
while (true)
{
Thread.Sleep(20);
string line = Console.ReadLine();
if (line != null)
{
process.StandardInput.WriteLine(line);
}
}
}
}
}