C# check if method executed [duplicate] - c#

This question already has answers here:
Wait until a process ends
(9 answers)
Wait for process to finish and then display message (C#)
(3 answers)
Closed 5 years ago.
I have a method which calls external exe file.
private void MainTask()
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "test.exe";
process.StartInfo = startInfo;
process.Start();
}
It can take 10-50-150 seconds to run test.exe file. The question is how can I check where it was executed or is still running.
I was trying to implement backgroundWorker like this:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
MainTask();
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pictureBoxOutput.Image =
MessageBox.Show("C Sharp is awesome.");
}
Is it possible to check when method is completely executed? Because right now I get a message "C Sharp is awesome." even though process is still running.
Edit: I want to create a progress bar and I need to update few elements on my form after process finishes. Process creates new files and I need to display new files' names. Of course it should finish first otherwise application will crash

You need to call process.WaitForExit()
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "test.exe";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit(); // this will block while the process is running

It all boils down to having a callback function after the the programm is finished. There are two ways for this:
Register the Process classes Exited Event.
If you want to keep your current BackgroundWorker code, you have to add a "wait for the Process to finish befor you continue" call inside the BackgroundWorker, as Mihail Shishkov showed in his answer.
Process.Start() will not wait for the process to exit. It will continue instantly. Effectively using Process.Start() is the same as hitting the "Ok" button on the Windows Run Dialog. Your code will instantly continue, regardless what the programm does from then on. In most cases you even have the Reference to the process instance run out of scope instantly after calling start.

You can use the Exited event
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "test.exe";
process.StartInfo = startInfo;
process.Exited += (sender, e) => {
pictureBoxOutput.Image = ...;
MessageBox.Show("C Sharp is awesome.");
};
process.Start();

Related

Linux process output redirection under .NET5/Core results in incorrect/broken behaviour

I have a very simple .NET5 Linux program (same behaviour on Core 3.1) which runs Nano and waits for it to terminate. If I redirect only one output stream then this code works as expected. Nano loads and I can interact exactly how I would if it was launched via Bash.
Now if I redirect stderr and stdout a number of odd things happen:
The nano window does not fill the entire terminal
Escape sequences appear on screen ^X as an example
I can use the menus but I have to press the escape sequence then hit enter
As I understood it stdout and stderr are different streams, why are they causing these issues? If I run:
nano 2>> stderr.txt
I can see that nano produces no stderr at all and everything works as expected.
I updated my code to only redirect stderr and as expected everything works. So this issue is something to do with redirecting both output streams. Code has been run under WSL1, WSL2 and a real machine.
Code that works:
static async Task Main(string[] args)
{
var proc = new Process();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "/bin/nano";
proc.StartInfo.Arguments = "hello";
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardError = false;
proc.StartInfo.RedirectStandardOutput = true; // only redirect stdout
proc.StartInfo.RedirectStandardInput = false;
proc.Start();
var t1 = proc.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
proc.WaitForExit();
await t1;
}
Code that doesn't:
static async Task Main(string[] args)
{
var proc = new Process();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "/bin/nano";
proc.StartInfo.Arguments = "hello";
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = false;
proc.Start();
var t1 = proc.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
var t2 = proc.StandardError.BaseStream.CopyToAsync(Console.OpenStandardError());
proc.WaitForExit();
await t1;
await t2;
}
I'm using nano as a good example here. The actual program works in a very similar way but produces stderr on output.

How to set Maximum method execution time

I've a method that contains a process that must be stopped in a deadline( ex: 3 seconds) whether it has finished or not, and I don't want to wait if it has finished executing before reaching that dead line.
using Process.WaitForExit(3000) makes the program wait 3s even if the process has stopped before reaching the limit.
One more thing, I'm using process.StandardOutput.ReadToEnd(); to read the execution result, I don't care if it returns null or empty string or whatever if it doesn't finish.
And I guess that timers will cause the same problem.
Any Ideas?
Exited event of your process can be handled for detecting exit time.
WaitForExit returns a Boolean value that indicates your process has reached the timeout before exit or not.
Test this code:
Process proc = new Process();
ProcessStartInfo procInfo = new ProcessStartInfo()
{
FileName = "d:/test.exe",
UseShellExecute = false,
RedirectStandardOutput = true
};
proc.StartInfo = procInfo;
proc.EnableRaisingEvents = true;
proc.Exited += (o, args) =>
{
MessageBox.Show(proc.StandardOutput.ReadToEnd());
};
proc.Start();
if (proc.WaitForExit(3000))
{
MessageBox.Show("YES");
}
else
{
MessageBox.Show("NO");
}

Can I read text from console application during console still runing process?

I use ProcessStartInfo to run a console aplication and ProcessStartInfo can read text from the console after the console is closed:
using (Process p = Process.Start(st))
{
//Thread.Sleep(2000);
p.WaitForExit();
using (StreamReader rd = p.StandardOutput)
{
result = rd.ReadToEnd();
p.Close();
String result1 = String.Copy(result);
}
Is there another method to read text from the console while it is open?
You can use the OutputDataReceived of the Process class
string result = string.Empty;
using (Process process = new Process())
{
process.StartInfo = st; // your ProcessStartInfo
StringBuilder resultBuilder = new StringBuilder();
process.OutputDataReceived += (sender, e) =>
{
st.AppendLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
result = resultBuilder.ToString();
}
You simply add an event handler to the Process's OutputDataReceived event that gets called whenever the process outputs a line.
Then you need to call BeginOutputReadLine() after the process has been started to begin receiving those events.
In this example, I still wait for the process to exit just to complete the code. Of course you don't need to wait, the events occure while the process is running. So you can store your process variable in a member and dispose it later or even subscribe to its Exited event to get informed when the process terminates.

Stop process blocking UI and return events as they happen

I'm trying to run a script and get the output as it happens to a log file. However, when I run the event it blocks the UI and waits till the end and spits out all of the output. Ideally i'd like to make it real time
public static void ExecuteCommandSync(object command)
{
try
{
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
Log.addLog(result);
}
catch (Exception objException)
{
Log.addLog(objException.Message);
}
}
Without a more detailed question, it's impossible to know for sure what advice you need. However, you write that you want to "get the output as it happens to a log file", which to me seems to mean that you don't want to have to wait until the process has exited to write output to the log file, but rather want to log the output as it occurs.
If so, you need to use one of the many possible asynchronous I/O mechanisms available with the Process class and its associated streams or readers.
For example:
public static async Task ExecuteCommandAsync(object command)
{
try
{
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
await ConsumeReader(proc.StandardOutput);
}
catch (Exception objException)
{
Log.addLog(objException.Message);
}
}
async static Task ConsumeReader(TextReader reader)
{
string text;
while ((text = await reader.ReadLineAsync()) != null)
{
Log.addLog(text);
}
}
The above will write the output to your log file asynchronously, as well as provide asynchronous notification of when the process completes. Naturally, for the latter to work, you will need to await the call to ExecuteCommandAsync(). If you don't need notification of the completion of the process, then you could just leave the method signature alone (i.e. make it simply static void), and then ignore the Task object returned by ConsumeReader().
If the above does not address your question, then please improve the question by providing a good Minimal, Complete, and Verifiable code example that shows clearly what you're trying to do, along with a precise description of what that code does and what you want it to do instead.
Please note also that there are already numerous questions on Stack Overflow regarding asynchronous use of Process output, and about avoiding blocking the UI thread in general. You likely can find additional details you seek by searching the site for those topics.
You can go all async:
public static async Task<string> ExecuteCommandAsync(object command)
{
try
{
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
return await proc.StandardOutput.ReadToEndAsync();
}
catch (Exception objException)
{
return objException.Message;
}
}
Then, to consume (in an async method):
var message = await ExecuteCommandAsync(command);
Log.addLog(message);

c# ProcessStartInfo.Start - reading output but with a timeout

If you want to start another process and wait (with time out) to finish you can use the following (from MSDN).
//Set a time-out value.
int timeOut=5000;
//Get path to system folder.
string sysFolder=
Environment.GetFolderPath(Environment.SpecialFolder.System);
//Create a new process info structure.
ProcessStartInfo pInfo = new ProcessStartInfo();
//Set file name to open.
pInfo.FileName = sysFolder + #"\eula.txt";
//Start the process.
Process p = Process.Start(pInfo);
//Wait for window to finish loading.
p.WaitForInputIdle();
//Wait for the process to exit or time out.
p.WaitForExit(timeOut);
//Check to see if the process is still running.
if (p.HasExited == false)
//Process is still running.
//Test to see if the process is hung up.
if (p.Responding)
//Process was responding; close the main window.
p.CloseMainWindow();
else
//Process was not responding; force the process to close.
p.Kill();
MessageBox.Show("Code continuing...");
If you want to start another process and read its output then you can use the following pattern (from SO)
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
How can you combine the two to read all input, not get stuck in deadlock and have a timeout if the running process goes awry?
This technique will hang if the output buffer is filled with more that 4KB of data. A more foolproof method is to register delegates to be notified when something is written to the output stream. I've already suggested this method before in another post:
ProcessStartInfo processInfo = new ProcessStartInfo("Write500Lines.exe");
processInfo.ErrorDialog = false;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;
Process proc = Process.Start(processInfo);
// You can pass any delegate that matches the appropriate
// signature to ErrorDataReceived and OutputDataReceived
proc.ErrorDataReceived += (sender, errorLine) => { if (errorLine.Data != null) Trace.WriteLine(errorLine.Data); };
proc.OutputDataReceived += (sender, outputLine) => { if (outputLine.Data != null) Trace.WriteLine(outputLine.Data); };
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
You don't have to combine the two - the Process class has an event that fires when output is sent to the StandardOutput - OutputDataReceived.
If you subscribe to the event, you will be able to read output as it arrives and in your main program loop you can still timeout.
you can try modifying the first method to something like this
Process p = Process.Start(pInfo);
string output = string.Empty;
Thread t = new Thread(() => output = p.StandardOutput.ReadToEnd() );
t.Start();
//Wait for window to finish loading.
p.WaitForInputIdle();
//Wait for the process to exit or time out.
p.WaitForExit(timeOut);
void OpenWithStartInfo()
{
ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe", "Default2.aspx");
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
Process p = Process.Start(startInfo);
p.WaitForInputIdle();
//p.WaitForExit(2);
p.Kill();
}
You could also use the APM, like this:
Define a delegate for the ReadToEnd call:
private delegate string ReadToEndDelegate();
Then use the delegate to call the method like this:
ReadToEndDelegate asyncCall = reader.ReadToEnd;
IAsyncResult asyncResult = asyncCall.BeginInvoke(null, null);
asyncResult.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(10));
asyncCall.EndInvoke(asyncResult);
EDIT: Error handling removed for clarity.
Just add everything from the first example below the WaitForExit() call to the second example.
None of the above answers work for me when dealing with interactive promts. (My command sometimes promts a question to the user and that should also be covered by timeout).
This is my solution.
A disadvantage is that i don't get any output if we run in a timeout.
ReadToEnd() blocks the execution so we have to run it in another thread and kill this thread if the process runs into the specified timeout.
public static Tuple<string, string> ExecuteCommand(string command)
{
// prepare start info
var procStartInfo = new ProcessStartInfo("cmd", "/c " + command)
{
ErrorDialog = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
WorkingDirectory = #"C:\",
CreateNoWindow = true
};
// start process
var proc = new Process {StartInfo = procStartInfo};
proc.Start();
var error = "";
var output = "";
// read stdout and stderr in new thread because it is blocking
Thread readerThread = new(() =>
{
try
{
error = proc.StandardError.ReadToEnd().Trim();
output = proc.StandardOutput.ReadToEnd().Trim();
}
catch (ThreadInterruptedException e)
{
Debug.WriteLine("Interrupted!!");
}
});
readerThread.Start();
// wait for max 6 seconds
if (proc.WaitForExit(6_000))
{
// if command runs to an enc => wait for readerThread to collect error/output stream
readerThread.Join();
}
else
{
// if process takes longer than 6 seconds => kill reader thread and set error to timeout
readerThread.Interrupt();
error = "Timeout!";
}
// return output and error
return new Tuple<string, string>(output, error);
}

Categories