Reputation: 51
I'm pretty much very new at C# and windows forms programming. I'm trying to build a very very simple form. I have only one button and a text box , When i click the button a process start at the background ( it's a process that i programmed in Python and compile to EXE file) , it's very simple process ... just print number from 1 to 4 in a 2 sec delay between each number I want the output to be display at the text box in a real-time meaning the number 1 to 4 in a 2 sec delay. I looked online and search a lot but couldn't find anything to help me with that. i read this thread -->How can I redirect process output (console) to richtextbox? and tried to implement what written there with no luck Thanks a lot! This is my code
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;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private StringBuilder sortOutput;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
String output="";
using (Process sortProcess = new Process())
{
sortProcess.StartInfo.FileName = @"c:/req/dist/ex/ex.exe";
sortProcess.StartInfo.CreateNoWindow = true;
sortProcess.StartInfo.UseShellExecute = false;
sortProcess.StartInfo.RedirectStandardOutput = true;
sortProcess.Start();
output = sortProcess.StandardOutput.ReadLine();
// sortProcess.WaitForExit();
while (!(sortProcess.HasExited))
{
richTextBox1.AppendText(output.ToString());
Application.DoEvents(); // This keeps your form responsive by processing events
}
}
//richTextBox1.AppendText(sortOutput.ToString());
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
//?????
}
}
}
Upvotes: 2
Views: 4504
Reputation: 53
I've found the problem mentionned in the response of Ivan Stoev. The textbox only update after process ended.
In fact, the code work pretty well with a batch file. But i faced the same issue with python code. The problem is in fact in the python code. After printing, you have to flush fflush(stdout).
So in my python code, i just had to add :
print("Something", flush=True)
And now it's work!
Upvotes: 0
Reputation: 205539
Process.BeginOutputReadLine
method in combination with Process.OutputDataReceived
event provide asynchronous behavior which fits perfectly in the UI component architecture. You don't need to to call a blocking WaitForExit
nor weird while
loop with Application.DoEevents
as in the referenced thread. All you need is to attach an event handler, call BeginOutputReadLine
and handle the received data inside the event. The only tricky (but at the same time standard for UI code) part is to make sure UI is updated only from the UI thread and do a little thread synchronization. Something like this:
using System;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private object syncGate = new object();
private Process process;
private StringBuilder output = new StringBuilder();
private bool outputChanged;
private void button1_Click(object sender, EventArgs e)
{
lock (syncGate)
{
if (process != null) return;
}
output.Clear();
outputChanged = false;
richTextBox1.Text = "";
process = new Process();
process.StartInfo.FileName = @"c:/req/dist/ex/ex.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += OnOutputDataReceived;
process.Exited += OnProcessExited;
process.Start();
process.BeginOutputReadLine();
}
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
lock (syncGate)
{
if (sender != process) return;
output.AppendLine(e.Data);
if (outputChanged) return;
outputChanged = true;
BeginInvoke(new Action(OnOutputChanged));
}
}
private void OnOutputChanged()
{
lock (syncGate)
{
richTextBox1.Text = output.ToString();
outputChanged = false;
}
}
private void OnProcessExited(object sender, EventArgs e)
{
lock (syncGate)
{
if (sender != process) return;
process.Dispose();
process = null;
}
}
}
}
EDIT The above method works only for lines and looks like your python exe is outputting characters. Here is a modified version that should work in that scenario:
using System;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private object syncGate = new object();
private Process process;
private StringBuilder output = new StringBuilder();
private bool outputChanged;
private void button1_Click(object sender, EventArgs e)
{
lock (syncGate)
{
if (process != null) return;
}
output.Clear();
outputChanged = false;
richTextBox1.Text = "";
process = new Process();
process.StartInfo.FileName = @"c:/req/dist/ex/ex.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
new Thread(ReadData) { IsBackground = true }.Start();
}
private void ReadData()
{
var input = process.StandardOutput;
int nextChar;
while ((nextChar = input.Read()) >= 0)
{
lock (syncGate)
{
output.Append((char)nextChar);
if (!outputChanged)
{
outputChanged = true;
BeginInvoke(new Action(OnOutputChanged));
}
}
}
lock (syncGate)
{
process.Dispose();
process = null;
}
}
private void OnOutputChanged()
{
lock (syncGate)
{
richTextBox1.Text = output.ToString();
outputChanged = false;
}
}
}
}
Upvotes: 3