Read from console process - c#

I have a process, i can start, and hide working fine, but i want to read from the console program, when i runs, not after, i tried to run a timer, anbd read at the tick, but my program just crashes and when it not do, i get nothing at all.
startInfo= new ProcessStartInfo("cmd.exe");
startInfo.Arguments ="/C uus.exe "+ arg.ToString();
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
this.timer1.Enabled=true;
this.listBox1.Items.Clear();
p= Process.Start(startInfo);
Application.DoEvents();
void Timer1Tick(object sender, EventArgs e)
{
string str="";
str=p.StandardOutput.ReadLine();
if(str != null)
{
this.Text=str.ToString();
this.listBox1.Items.Add(str);
}
Application.DoEvents();
}
So what do i do to solve this?
Update:
I tried bender suggestion
now My program don't crash anymore, but also don't recvie any data
proc.StartInfo.UseShellExecute=false;
proc.StartInfo.CreateNoWindow=true;
proc.StartInfo.RedirectStandardOutput=true;
proc.StartInfo.RedirectStandardError=true;
proc.StartInfo.FileName="uus.exe";
proc.StartInfo.Arguments=arg;
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(SortOutputHandler);
proc.Start();
proc.BeginOutputReadLine();
void SortOutputHandler(object o,System.Diagnostics.DataReceivedEventArgs e)
{
string str="";
string str2="";
str=e.Data.ToString();
if(str!=null && str!="")
{
this.listBox1.Items.Add(str.ToString());
this.Text=str.ToString();
}
str2=proc.StandardOutput.ReadLine();
if(str2!=null && str2!="")
{
this.lsw1.Items.Add(str2.ToString());
}
}
hmm?
Update:
I have changed the handler, because i have being tell, it can't do it, that it wil be cross thread operation, usualyy i wille have get an error if it was.
private delegate void TextAdderDelegate(string str);
void TextAdder(string str)
{
if(this.lsw1.InvokeRequired==true)
{
Invoke(new TextAdderDelegate(TextAdder),new object[] {str});
}
else
{
this.lsw1.Items.Add(str);
}
}
void SortOutputHandler(object o,System.Diagnostics.DataReceivedEventArgs e)
{
string str="";
if(e!=null)
{
if(e.Data!=null)
{
str=e.Data.ToString();
}
}
TextAdder(str);
}

The problem is that you're running on one thread and trying to write using another. When you created your background thread using the Timer's tick event, it can't have frontend user input.
Perhaps if you explained the big picture of what you're trying to accomplish, we can better help you.
In the meantime, you might want to create threadsafe writes. This article will help you to understand the problem and solution to writing to form controls on different threads.

You may create the Process instance explicitly (e.g. new Process)and use the OutputDataReceived event, the method BeginOutputReadLine() and, when finished CancelOutputRead() for that.
The event OutputDataReceived will be repeatedly called asynchronously from a different thread as soon output data is available.

I assume you get an 'thread cross exception', this may be caused because you're updating your form controls on an other thread then the UI thread.

Related

Winform progress bar only updating when process is finished (C#, Python)

Using a C# Winform as GUI to run my Python script in the background. Currently am applying a backgroundworker to handle it in another thread and keep the GUI responsive; however, the progress bar only seems to update when the process is finished. Stays at Value=0 and then changes to Value=100 once the process terminates; would much prefer have it increase as python executes the background process so I know it's still alive and working..
My understanding is that blocking should not be occurring because the backgroundworker is moving the process to a separate thread, so I'm not too sure what could be stopping it from updating.
Here's the relevant sections of my C# code:
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
----
private void button_test2_Click(object sender, EventArgs e) // this is the active button
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
----
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
//break;
}
else
{
// Cutting out some stuff here for brevity. Just assigns arg1-4 based on inputs from the GUI, nothing crazy
// Python script to run
process.StartInfo.FileName = #"C:\blablabla\python.exe";
string script = #"C:\paths\pythonscriptgoeshere.py";
process.StartInfo.Arguments = $"\"{script}\" \"{arg1}\" \"{arg2}\" \"{arg3}\" \"{arg4}\"";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
while (!process.StandardOutput.EndOfStream)
{
bool ItsAnInt = Int32.TryParse(process.StandardOutput.ReadLine(),out int PyOutput);
if (ItsAnInt) // I know this part is ugly lol - just making sure only numbers/percentages are being passed
{
backgroundWorker1.ReportProgress(PyOutput);
// PyOut is just a percentage (0-100) of how many iterations it has completed out of the total
}
}
//string stderrx = process.StandardError.ReadToEnd();
//string stdoutx = process.StandardOutput.ReadToEnd();
//MessageBox.Show($"Output: {stdoutx}\nError: {stderrx}");
}
---
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Not quite sure why the progressbar1.Value isn't being updated from backgroundWorker1_ProgressChanged since it seems to be called while the process is actively running. Anyway, sorry for the long post and thanks for any help.
Cheers!
EDIT: Thank you #Jimi for helping me figure this out in the comments. The issue was that the UI was flooded with request since the test data set I was using was computed faster than the progressbar could update so it essentially didn't get a chance to breathe and catch up. Solution was to add in a Thread.Sleep(1) every handful of iterations to let it update.
EDIT 2: The plot thickens! Turns out that the progress bar would give me updates like I wanted, but wouldn't start the processes of updating until the background process was already done. Turns out what I needed to do was make a second background worker as well as an external python script that would read outputs and then report them to the bar. Not the best solution I'm sure but it seemed to do the trick for me :-)

Executing a method simultaneously to program window and stop it anytime

I need a functionality that will allow to execute method in a background and leave window responsive, but I need to have a possibility to stop or suspend it anytime. I know that threads are partly answer to my question, but unfortunately there is no way to stop thread from executing a time-absorbing block of code just like that. I had thoughts about process communication, but is it a good idea? Or maybe there is a way to terminate a thread unconditionally?
The only option that you have, if it's important that you can always stop the code at any point in it's execution, and when you can't have cooperative cancellation on the part of the worker, then you need to have a separate process. It is the most reliable way of stopping the execution of code in the manor you've described. It cannot be reliably done using Threads.
It seems that you're looking for BackgroundWorker .
You'll have to check in the second thread if the main thread is asking it to stop, and do so if needed.
Simple (tested) example:
public partial class Form1 : Form
{
BackgroundWorker w = new BackgroundWorker();
public Form1()
{
InitializeComponent();
w.WorkerSupportsCancellation = true;
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.RunWorkerAsync();
}
void w_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10000000000; i++)
{
if (w.CancellationPending)
{
MessageBox.Show("Cancelled");
break;
}
//Do things...
}
}
private void button1_Click(object sender, EventArgs e)
{
w.CancelAsync();
}
}
EDIT
If you're speaking of an HTTP request, perhaps: HttpWebRequest.Abort? (Though see this answer.)
As stated in comments thread.Abort() is an option, but not advised. Rather you should be using thread.Interrupt() and the reasons for that are well detailed here.
The reason you should not kill a thread instantly is because it could cause a lock being set, but never unset due to the fact that the code was suddenly stopped with no path out. So if it locks code that you will need to reuse, there would be no way to unlock it from the previous call. Obviously, you could build around this, but I'm assuming you are using blocking code that isn't built from the ground up by you.
You could do it in a separate process and kill the process with much less risk, but then passing the data back and forth and the added mess becomes complicated.
Here is an example of using an external process to do this chuck and being able to kill the process will less risk.
public class Main
{
public Main()
{
//In use
DoCalculation calc = new DoCalculation();
calc.StartCalculation(123, 188, ReceivedResults);
//Cancel after 1sec
Thread.Sleep(1000);
calc.CancelProcess();
}
public void ReceivedResults(string result)
{
Console.WriteLine(result);
}
}
public class DoCalculation
{
private System.Diagnostics.Process process = new System.Diagnostics.Process();
private Action<string> callbackEvent;
public void StartCalculation(int var1, int var2, Action<string> CallbackMethod)
{
callbackEvent = CallbackMethod;
string argument = "-v1 " + var1 + " -v2 " + var2;
//this is going to run a separate process that you'll have to make and
//you'll need to pass in the argument via command line args.
RunProcess("calcProc.exe", argument);
}
public void RunProcess(string FileName, string Arguments)
{
SecurityPermission SP = new SecurityPermission(SecurityPermissionFlag.Execution);
SP.Assert();
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = FileName;
process.StartInfo.Arguments = Arguments;
process.StartInfo.WorkingDirectory = "";
process.OutputDataReceived += ProcessCompleted;
process.Start();
}
public void CancelProcess()
{
if (process != null)
process.Kill();
}
private void ProcessCompleted(object sender, DataReceivedEventArgs e)
{
string result = e.Data;
if (callbackEvent != null)
{
callbackEvent.Invoke(result);
}
}
}
Can you give us any more details on exactly what you are doing? Perhaps there are better alternatives to this problem.

How to determine if a Process has started but not yet exited?

I have some code that creates a Process instance and later starts it. There's some logic that need to check if the Process has been started. HasExited can be used to check if a started process has been exited, but I can not find a similar function for HasStarted. At first glance StartTime looked like a good option, but this function will throw if the process has exited. Also, the documentation says that StartTime only has meaning for started processes.
What is the "correct" approach for determining if a process has started (has been started, but might have quit)?
While the methods suggested by others will work, it is not the most efficient way to handle such things. If you keep a loop checking whether the Process has exited or not, you will waste a lot of system resources.
Your concern should be to just know when the process is exiting, and not sit looping for it to check whether it has exited. So, the correct way is to handle Events.
The code below explains how to do that using Events.
// Declare your process object with WithEvents, so that events can be handled.
private Process withEventsField_MyProcess;
Process MyProcess {
get { return withEventsField_MyProcess; }
set {
if (withEventsField_MyProcess != null) {
withEventsField_MyProcess.Exited -= MyProcess_Exited;
}
withEventsField_MyProcess = value;
if (withEventsField_MyProcess != null) {
withEventsField_MyProcess.Exited += MyProcess_Exited;
}
}
}
bool MyProcessIsRunning;
private void Button1_Click(System.Object sender, System.EventArgs e)
{
// start the process. this is an example.
MyProcess = Process.Start("Notepad.exe");
// enable raising events for the process.
MyProcess.EnableRaisingEvents = true;
// set the flag to know whether my process is running
MyProcessIsRunning = true;
}
private void MyProcess_Exited(object sender, System.EventArgs e)
{
// the process has just exited. what do you want to do?
MyProcessIsRunning = false;
MessageBox.Show("The process has exited!");
}
EDIT:
Knowing whether the process has started or not should be easy since are starting the process somewhere in the code. So you can set a flag there and set it to false when the process is exiting. I updated the code above to show how such a flag can be set easily.
Search your process in Process.GetProcesses();, the list returned by this method give all processes currently running on the machine.
You can use the Process.GetProcesses method (in the System.Diagnostics
namespace) to get a list of processes currently running on the PC.
Process.GetProcessesByName() can also be used to just get a list of
instances of a particular program.
// Get all instances of Notepad running on the local computer.
Process [] localByName = Process.GetProcessesByName("YourProcess");
You could check that there is atleast one thread in the process. This would indicate that the process is started and running.
Edit:
You could also check the process Id. It will throw an exception if the process hasn't started.
Edit 2:
Actually Threads will also throw an exception if the Id is not set:
bool ProcessIsRunning(Process p)
{
bool isRunning;
try {
isRunning = !p.HasExited && p.Threads.Count > 0;
}
catch(SystemException sEx)
{
isRunning = false;
}
catch(PlatformNotSupportedException pnsEx)
{
throw;
}
return isRunning;
}

How to pass objects between threads when running a System.Diagnostics process

I won't provide all code, but an example of what I want to do. I have this code for updating GUI elements from an external processes stderr.
I set up my process like this:
ProcessStartInfo info = new ProcessStartInfo(command, arguments);
// Redirect the standard output of the process.
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.CreateNoWindow = true;
// Set UseShellExecute to false for redirection
info.UseShellExecute = false;
proc = new Process();
proc.StartInfo = info;
proc.EnableRaisingEvents = true;
// Set our event handler to asynchronously read the sort output.
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
proc.Exited += new EventHandler(proc_Exited);
proc.Start();
// Start the asynchronous read of the sort output stream. Note this line!
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
I then have an event handler
void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
UpdateTextBox(e.Data);
}
}
Which invokes the following, which references a specific textbox control.
private void UpdateTextBox(string Text)
{
if (this.InvokeRequired)
this.Invoke(new Action<string>(this.SetTextBox), Text);
else
{
textBox1.AppendText(Text);
textBox1.AppendText(Environment.NewLine);
}
}
What I want is something like this:
private void UpdateTextBox(string Text, TextBox Target)
{
if (this.InvokeRequired)
this.Invoke(new Action<string, TextBox>(this.SetTextBox), Text, Target);
else
{
Target.AppendText(Text);
Target.AppendText(Environment.NewLine);
}
}
That I can use to update different Textboxes with from that thread, without having to create a seperate function for every control in the GUI.
Is this possible? (obviously the code above does not work correctly)
Thanks.
UPDATE:
private void UpdateTextBox(string Text, TextBox Target)
{
if (this.InvokeRequired)
this.Invoke(new Action<string, TextBox>(this.**UpdateTextBox**), Text, Target);
else
{
Target.AppendText(Text);
Target.AppendText(Environment.NewLine);
}
}
This code does appear to work now as I noticed a typo.. is this ok to use?
The code you provided looks good and is good way to send such messages between threads.
Take a look at this.
http://weblogs.asp.net/justin_rogers/pages/126345.aspx
I have done this before, but I don't have the code with me right now.
if I get to it I will post it for you, but the article might have enough information for you to figure out.

cmd.exe style application written in C#

I am trying to get the results from any DOS-based application, effectively letting C# operate it as if it were a user.
I can get it to execute a command, and then show the output. The problem is knowing when the output has ended! For example, if I go start/run "cmd.exe", type "D:", then "cd D:\", and then "tree", it outputs my folder structure on the D drive, and then allows me to type my next command (only after it's finished printing the list).
However I can't find a way in code to get it to realise it's finished, and should allow the next command (basically when cmd.exe starts blinking your cursor).
public Process p = null;
private void Form1_Load(object sender, System.EventArgs e)
{
ProcessStartInfo procStarter = new ProcessStartInfo("cmd.exe");
procStarter.RedirectStandardOutput = true;
procStarter.RedirectStandardInput = true;
procStarter.UseShellExecute = false;
procStarter.CreateNoWindow = true;
p = Process.Start(procStarter);
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
p.Close();
}
private void btnSend_Click(object sender, System.EventArgs e)
{
p.StandardInput.WriteLine("D:");
p.StandardInput.WriteLine(#"cd D:\");
txtOutput.Text = SendCommand(txtInput.Text);
}
private string SendCommand(string cmd)
{
p.StandardInput.WriteLine(cmd);
return p.StandardOutput.ReadToEnd();
}
In SendCommand(string cmd), if I run p.StandardOutput.ReadToEnd(), as per the code above, it hangs forever, presumably waiting for the application to close?
If I loop through p.StandardOutput.ReadLine(), it shows all the text (including the "D:\>" just before where the blinking cursor would then be, however it doesn't realise it's the end, calls ReadLine again, and hangs in a smiliar fashion to ReadToEnd. A dirty workaround would be to treat it as the end of the response if the current line ends with ">", however that falls apart if a line ends like that anywhere in the response.
I've tried looping through the stream character by character, and there's no special character sent at the end.
Any ideas?
Note: My ultimate goal is to provide a light library I can use for executing any DOS executable (which may require several typed commands passed to it, not just the one off arguments passed via command line on opening), parsing the results returned with a regex template, and returning the extracted result. I figured if I can effectively re-implement the cmd.exe in a windows application, then such a library will be possible.
Thanks,
Lee
I suspect that your approach doesn’t work. cmd.exe is not going to communicate to you via StandardOutput when or whether the command you ran has finished or not. (I should point out though that this doesn’t stop you from running multiple commands. You can probably just send the command lines and don’t actually need to wait for it to finish.)
Perhaps a more suitable approach might be not to use cmd.exe at all. Instead, use Process.Start() to run each individual command. Then you can use StandardOutput.ReadToEnd() and it will finish when the process is finished, and you can run the next one.
I agree with Timwi, But see if something like below helps
ProcessStartInfo procStarter = new ProcessStartInfo("cmd.exe");
procStarter.RedirectStandardOutput = true;
procStarter.RedirectStandardInput = true;
procStarter.UseShellExecute = false;
procStarter.CreateNoWindow = true;
procStarter.WorkingDirectory = #"D:\";
procStarter.Arguments = "/C dir";
Process p = Process.Start(procStarter);
string output = p.StandardOutput.ReadToEnd();
/C command line to cmd.exe will terminate cmd.exe once the work is done. You can also use p.Exited (exited event) to know when it happens.
However it will not keep the cmd.exe always running. But do you really need to keep it running?
If you're looking for 'how to wait till the spawned process terminates', Process.WaitForExit is what should do the trick.
You could spawn a new shell for each "command".
About a year ago I wrote a telnet server for windows that allowed the remote user to issue commands against cmd.exe. Maybe you can use it as a starting point for your own project.
Get the code on my blog
By reading the output asynchronous I have gotten this to work (aleast almost) like you described:
public Process p = null;
private void Send_Click(object sender, System.EventArgs e)
{
p.StandardInput.WriteLine("D:");
p.StandardInput.WriteLine(#"cd D:\");
p.StandardInput.WriteLine(txtInput.Text);
}
private void Form1_Load_1(object sender, EventArgs e)
{
ProcessStartInfo procStarter = new ProcessStartInfo("cmd.exe");
procStarter.RedirectStandardOutput = true;
procStarter.RedirectStandardInput = true;
procStarter.UseShellExecute = false;
procStarter.CreateNoWindow = true;
p = Process.Start(procStarter);
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.BeginOutputReadLine();
}
void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
addTextToOutput(e.Data);
}
private void addTextToOutput(string text)
{
if (txtOutput.InvokeRequired)
{
addTextCallback cb = new addTextCallback(addTextToOutput);
this.Invoke(cb, new Object[] { text });
}
else
{
txtOutput.Text += text+ System.Environment.NewLine;
}
}
delegate void addTextCallback(String text);
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
p.Close();
}

Categories