How to get a process started by another process I started? - c#

I know the title is alittle confusing but I have a problem with getting a certain process that gets created by a process I start in my code and here is what I mean ..
private void BtnUNINSTALLG_Click(object sender, EventArgs e)
{
LoadingGif1.Visible = true;
Process p = Process.Start(#"C:\program files\XXXX\XXXX\XXX\XXXX.exe");
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(UNINSTALL_Exited);
}
private void UNINSTALL_Exited(object sender, System.EventArgs e)
{
Invoke(new Action(() => {
LoadingGif1.Visible = false;
DoneGif1.Visible = true;
}));
}
What I do here is I start a process and after it exits I do some stuff .. but that doesn't work because this process create temp.exe ( changes name every time ) in temp folder and the process I started exits right after this temp process created.
I want to get the temp process created by the process I started and then wait until it exits instead of the process I started. Does anyone have any idea how can I do that ?

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 :-)

How to stop a C# Process that is long and does not loop

I have a specific situation where I have a process that calls a OS command and I need to stop or kill it. Let's say for example it is a continuous process. The process executed "myapplication.exe" for example. Should I not use the background worker and just wrap it in a thread. Then kill the thread? I also thought about sending a CTRL+C to cancel but not sure how to inject that into a process command. What would be the right path?
private void btnExecuteResponseCmd_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
//https://stackoverflow.com/questions/4732737/how-to-stop-backgroundworker-correctly
//works if it is looping and can read the cancel variable but not in this case
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ProcessStartInfo pStartInfo = new ProcessStartInfo("myapplication.exe");
//https://social.msdn.microsoft.com/Forums/en-US/5880a108-4169-44a5-81f2-6a745438d486/redirecting-command-window-messages-to-rich-text-box
pStartInfo.CreateNoWindow = true;
pStartInfo.UseShellExecute = false;
pStartInfo.RedirectStandardInput = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.RedirectStandardError = true;
process1.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process1.ErrorDataReceived += new DataReceivedEventHandler(ErrorHandler);
process1.StartInfo = pStartInfo;
process1.SynchronizingObject = rbResponse;
process1.Start();
process1.BeginOutputReadLine();
process1.WaitForExit();
}
Ok, I found how to control the cancellation and looks like it kills the process just fine. I will have to explore if it releases all resources, and this is the best way to do it.
private void btnStopCommand_Click(object sender, EventArgs e)
{
process1.CancelOutputRead();
process1.Kill();
}

Update C# form RichTextBox content (in realtime) with the output of the batch file execution without waiting for the batch command execution to finish

Below is my C# code in the form.
private void callInfraCommand(string cmd)
{
string os_name = GetOSFriendlyName();
var m_command = new System.Diagnostics.Process();
// set up output redirection
m_command.StartInfo.RedirectStandardOutput = true;
m_command.StartInfo.RedirectStandardError = true;
m_command.StartInfo.CreateNoWindow = true;
m_command.StartInfo.UseShellExecute = false;
m_command.StartInfo.CreateNoWindow = true;
m_command.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
m_command.EnableRaisingEvents = true;
// see below for output handler
m_command.ErrorDataReceived += proc_DataReceived;
m_command.OutputDataReceived += proc_DataReceived;
if (os_name.Contains("Windows 7") || os_name.Contains("2008") || os_name.Contains("Windows 8") || os_name.Contains("2012"))
m_command.StartInfo.FileName = #"C:\windows\system32\cmd.exe";
else
m_command.StartInfo.FileName = #"C:\WINNT\system32\cmd.exe";
m_command.StartInfo.Arguments = cmd;
m_command.Start();
m_command.BeginErrorReadLine();
m_command.BeginOutputReadLine();
m_command.WaitForExit();
}
private void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
// output will be in string e.Data
if (e.Data != null)
this.BeginInvoke(new Action(() => deploy_result_txt.Text += (Environment.NewLine + e.Data)));
}
I am passing the windows batch file to callInfraComamnd and it's block of code is working fine. But, my batch file is taking more than 5 minutes to finish and during that time, my form is freezing and after the batch process completes it displaying all the output of the batch file in chunk.
Ideally, i would like to see the progress (intermediate output of the batch file) in the TextBox while the batch process is running in the background.
Can anyone please advise how can I resolve it (i am using .NET 4) ?
You need to execute the content of private void callInfraCommand(string cmd) in a Task. The m_command.WaitForExit() blocks the thread calling this function (the UI thread). Since it's blocked it cannot process the marshalled code in proc_DataReceived() before m_command.WaitForExit() finished.
Option 1: don't wait for exit which would block the UI thread
private void callInfraCommand(string cmd)
{
//...
m_command.BeginErrorReadLine();
m_command.BeginOutputReadLine();
// m_command.WaitForExit();
}
Option 2: Call the blocking part in a separate task
private void callInfraCommand(string cmd)
{
//...
m_command.BeginErrorReadLine();
m_command.BeginOutputReadLine();
Task.Factory.StartNew(
() =>
{
m_command.WaitForExit()
// code to clean up etc.
// if there is nothing in here, there's no need to call WaitForExit()!
}
);
}
UPDATE:
Option 3: Do something after the process finished which is defined elsewhere.
private void callInfraCommand(string cmd, Action afterFinishedAction)
{
//...
m_command.BeginErrorReadLine();
m_command.BeginOutputReadLine();
Task.Factory.StartNew(
() =>
{
m_command.WaitForExit()
afterFinishedAction();
}
);
}
#Sam Then I suggest you create a task which contains the loop over each process call and leave the waitForExit.

C# Is it possible to close that process?

I have this code:
private void button1_Click(object sender, EventArgs e)
{
Process p = new Process();
string path = AppDomain.CurrentDomain.BaseDirectory + "samp.exe";
string arguments = "arguments...";
p.Exited += new EventHandler(p_Exited);
p.StartInfo.FileName = path;
p.EnableRaisingEvents = true;
p.Start();
}
And
void p_Exited(object sender, EventArgs e)
{
MessageBox.Show("it works");
}
It works, but when I launch samp.exe, the name changes to gta_sa.exe. I want to check if gta_sa.exe process was closed, then close my app.
SHORTLY: I want to make a button. When I click on, it launches samp.exe process, but samp.exe renames to gta_sa.exe, so I need to check if gta_sa.exe process was closed, close my app (Test.exe)
My code is closing samp.exe, but I want to close gta_sa.exe.
It work's, but when launches samp.exe, name changes to gta_sa.exe.
It sounds like samp.exe is a launcher for gta_sa.exe. That is, the first process starts the second process. If samp.exe does not wait for gta_sa.exe to exit, you will have to find the running instance of gta_sa.exe. You can then create a Process instance from the running process and add an event handler for Exited.
P.S my code closing samp.exe, but I wan't to close gta_sa.exe..
No, it is not. Your code is being alerted when samp.exe closes on its own (or for some other reason). If samp.exe is indeed a launcher, its normal behavior would be to close after it starts gta_sa.exe.
If you want to close gta_sa.exe, you can do that using Process.Kill().
You can set an event handler for gta_sa.exe closing like this
var processes = Process.GetProcessesByName("gta_sa.exe");
foreach (var p in processes)
{
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(p_Exited);
}
Make sure you wait to run this code until after gta_sa.exe has been started. Normally there will be only one item in processes, the one process that was launched by samp.exe.
When your samp.exe process exits try to get "gta_sa.exe" process and terminate it:
void p_Exited(object sender, EventArgs e)
{
var processes = Process.GetProcessesByName("gta_sa.exe");
foreach (var process in processes)
process.Kill();
}

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