38

I am new to the thread model in .NET. What would you use to:

  1. Start a process that handles a file (process.StartInfo.FileName = fileName;).
  2. Wait for the user to close the process OR abandon the thread after some time.
  3. If the user closed the process, delete the file.

Starting the process and waiting should be done on a different thread than the main thread, because this operation should not affect the application.

Example:

My application produces an html report. The user can right click somewhere and say "View Report" - now I retrieve the report contents in a temporary file and launch the process that handles html files i.e. the default browser. The problem is that I cannot cleanup, i.e. delete the temp file.

7
  • Console ap or WinForms or ASP.NET? Commented Mar 4, 2009 at 15:42
  • Console or WinForms. The process I am starting is local to the machine. Commented Mar 4, 2009 at 15:47
  • I have an example that uses async/await posted up here-- allampersandall.com/2013/03/net-process-async-await Commented Mar 4, 2013 at 20:06
  • You might be interested in this post, which explains how to work with a Process using async/await and timeouts. Commented Aug 29, 2014 at 23:49
  • 1
    possible duplicate of Is there any async equivalent of Process.Start? Commented Oct 1, 2014 at 5:07

6 Answers 6

61

"and waiting must be async" - I'm not trying to be funny, but isn't that a contradiction in terms? However, since you are starting a Process, the Exited event may help:

ProcessStartInfo startInfo = null; Process process = Process.Start(startInfo); process.EnableRaisingEvents = true; process.Exited += delegate {/* clean up*/}; 

If you want to actually wait (timeout etc), then:

if(process.WaitForExit(timeout)) { // user exited } else { // timeout (perhaps process.Kill();) } 

For waiting async, perhaps just use a different thread?

ThreadPool.QueueUserWorkItem(delegate { Process process = Process.Start(startInfo); if(process.WaitForExit(timeout)) { // user exited } else { // timeout } }); 
Sign up to request clarification or add additional context in comments.

8 Comments

I have a question. If i have a function A() { ThreadPool.UnsafeQueueUserWorkItem((o) => { .... proc.WaitForExit(); ... } and in the main method I have like this : A(); Console.Write("aa"); for example... the thing is when console.write finish its work and the thread still didnt finish its work.. the page is loading. and its not waiting for the process to termine. what should i do ? Thanks guys
@Grace hard to say based just on that... but unless you wait for the worker thread, isn't that exactly what you would expect to see?
so i should wait for the thread to termine from the main method ?
@Grace that depends on what you are doing; if the only thing you are doing is waiting for a process to exit, then... why do that on a worker thread? Needs more context, basically. Otherwise I can't give a useful answer.
:my process converts files to .flv and plays them with javascript.. when i call the function without a Thread.Sleep() after it , i cant get the conversion nor the player bcoz the thread didnt have time to finish its work before the page load..butwhen i put a sleep after the function , thats when im getting my player.
|
23

Adding an advanced alternative to this old question. If you want to wait for a process to exit without blocking any thread and still support timeouts, try the following:

 public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout) { ManualResetEvent processWaitObject = new ManualResetEvent(false); processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); RegisteredWaitHandle registeredProcessWaitHandle = null; registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject( processWaitObject, delegate(object state, bool timedOut) { if (!timedOut) { registeredProcessWaitHandle.Unregister(null); } processWaitObject.Dispose(); tcs.SetResult(!timedOut); }, null /* state */, timeout, true /* executeOnlyOnce */); return tcs.Task; } 

Again, the advantage to this approach compared to the accepted answer is that you're not blocking any threads, which reduces the overhead of your app.

Comments

4

The .NET 5 introduced the new API Process.WaitForExitAsync, that allows to wait asynchronously for the completion of a process. It offers the same functionality with the existing Process.WaitForExit, with the only difference being that the waiting is asynchronous, so it does not block the calling thread.

Usage example:

private async void button1_Click(object sender, EventArgs e) { string filePath = Path.Combine ( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Guid.NewGuid().ToString() + ".txt" ); File.WriteAllText(filePath, "Hello World!"); try { using Process process = new(); process.StartInfo.FileName = "Notepad.exe"; process.StartInfo.Arguments = filePath; process.Start(); await process.WaitForExitAsync(); } finally { File.Delete(filePath); } MessageBox.Show("Done!"); } 

In the above example the UI remains responsive while the user interacts with the opened file. The UI thread would be blocked if the WaitForExit had been used instead.

6 Comments

If I would need this on lower versions of .NET, I would probably copy the source in an extension method. source.dot.net/#System.Diagnostics.Process/System/Diagnostics/…
@BogdanGavrilMSFT in case it's about starting one process only, I would probably prefer the dirty but safe await Task.Run(() => process.WaitForExit());. 😃
@TheodorZoulias No. That has zero advantage over the blocking call to WaitForExit(), with that additional overhead of await. That isn't safe. Don't do that.
@MarcL I am not sure what you are comparing. Are you saying that the asynchronous Process.WaitForExitAsync has zero advantage over the blocking Process.WaitForExit, and also that it's not safe?
I'm saying that await Task.Run(<synchronous method>) is not a good alternative, since it queues the work onto the ThreadPool. See Stephen Cleary for more details.
|
3

Try the following code.

public void KickOffProcess(string filePath) { var proc = Process.Start(filePath); ThreadPool.QueueUserWorkItem(new WaitCallBack(WaitForProc), proc); } private void WaitForProc(object obj) { var proc = (Process)obj; proc.WaitForExit(); // Do the file deletion here } 

1 Comment

Not sure it handles the "OR abandon the thread after some time"
0

I would probably not use a separate process for opening a file. Instead, I'd probably utilize a background thread (if I thought the operation was going to take a long time and possible block the UI thread).

private delegate void FileOpenDelegate(string filename); public void OpenFile(string filename) { FileOpenDelegate fileOpenDelegate = OpenFileAsync; AsyncCallback callback = AsyncCompleteMethod; fileOpenDelegate.BeginInvoke(filename, callback, state); } private void OpenFileAsync(string filename) { // file opening code here, and then do whatever with the file } 

Of course, this is not a good working example (it returns nothing) and I haven't shown how the UI gets updated (you have to use BeginInvoke at the UI level because a background thread cannot update the UI thread). But this approach is generally how I go about handling asynchronous operations in .Net.

1 Comment

Sorry, actually I meant that the process should handle the file, not open it. I rewrote the question.
0

You can use the Exited event in Process class

ProcessStartInfo info = new ProcessStartInfo(); info.FileName = "notepad.exe"; Process process = Process.Start(info); process.Exited += new EventHandler(process_Exited); Console.Read(); 

and in that event you can handle the operations you mentioned

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.