none
Process.WaitForExit() doesn't actually wait for the process to completely exit? RRS feed

  • Question

  • Hi,

    I have a situation where I want to execute a file, and then delete the executable file when the execution is done.
    I use System.Diagnostics.Process.Start() to execute the the file, and use .WaitForExit() to wait for the process to complete.
    I then use File.Delete(filename) to delete the executable file.

    The problem is that sometimes, File.Delete will fail with the following error:

    Unhandled Exception: System.UnauthorizedAccessException: Access to the path 'c:\workspace\ss.exe' is denied.
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.File.InternalCopy(String sourceFileName, String destFileName, Boolean overwrite)
       at System.IO.File.Copy(String sourceFileName, String destFileName)
       at ConsoleApplication1.Program.Main() in C:\workspace\test\ConsoleApplication1\Program.cs:line 22

    This leads me to believe that, somehow, the process is still alive and locking the executable file even after the .WaitForExit() call returns.

    I have found other threads on the net that describe the same behavior, but I have not found any that conclude with a reasonable explanation or solution to the problem, other than suggestions of placing a Sleep(n) after the WaitForExit().  Examples:

    http://stackoverflow.com/questions/1310791/deletefile-of-an-exe-immediately-after-process-waitforexit-fails
    http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/msg/4e18b2bcdae598c4

    Here is a sample program that reproduces the problem for me.  I have tested on both Windows XP and Windows 7, and they both eventually fail at some point with the same error.

    Also note that the ss.exe executable is a plain simple "Hello World" console application...

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Linq;
    using System.IO;
    using System.Diagnostics;
    using System.Threading;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main()
            {
                const string filenamepart = @"c:\workspace\ss";
                const string filename = filenamepart + ".exe";
    
                Console.WriteLine("start");
    
                for (int i = 0; i < 100; i++)
                {
                    File.Copy(filenamepart, filename);
                    Console.WriteLine(i + ". " + RunProcess(filename));
                    File.Delete(filename); // will some times crash on this line...
                }
    
                Console.WriteLine("end");
            }
    
            private static int RunProcess(string filename)
            {
                ProcessStartInfo startInfo = new ProcessStartInfo(filename);
                startInfo.RedirectStandardError = true;
                startInfo.RedirectStandardOutput = true;
                startInfo.UseShellExecute = false;
    
                using (Process p = new Process())
                {
                    p.StartInfo = startInfo;
                    p.Start();
    
                    p.WaitForExit();
                    // Thread.Sleep(1000);  // adding the sleep here makes the problem go away...
    
                    Console.Write(p.StandardOutput.ReadToEnd());
                    Console.Write(p.StandardError.ReadToEnd());
    
                    return p.ExitCode;
                }
            }
        }
    }
    


    and the output typically looks something like (with varying amount of iterations before the crash occurs):

    start
    hello world
    0. 0
    hello world
    1. 0
    hello world
    2. 0
    hello world
    3. 0
    hello world
    4. 0
    hello world
    5. 0
    hello world
    6. 0
    hello world
    7. 0
    hello world
    8. 0
    hello world
    9. 0
    hello world
    10. 0
    hello world
    11. 0
    hello world
    12. 0
    hello world
    13. 0
    
    Unhandled Exception: System.UnauthorizedAccessException: Access to the path 'c:\workspace\ss.exe' is denied.
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.File.InternalCopy(String sourceFileName, String destFileName, Boolean overwrite)
       at System.IO.File.Copy(String sourceFileName, String destFileName)
       at ConsoleApplication1.Program.Main() in C:\workspace\test\ConsoleApplication1\Program.cs:line 22
    Press any key to continue . . .

    Any ideas on why this happens, and if there is some fix for this (other than putting sleeps in the code)?

    Thanks
    Sam
    Friday, January 8, 2010 3:40 PM

Answers

  • The reason is that although the process completes, it may take a few milliseconds for the OS to finish destroying it. It's a multi-tasking OS after all, it's trying to service everyone at the same time. Unfortunetely, there is no way to know when the file is no longer locked (presicely). When you WaitForExit, you are creating a lock object on the process object. The process object cannot be destroyed until the lock is released and you cannot wait on something that doesn't exist (chicken and egg kind of issue).

    So the only reasonable thing you can do is keep trying, something like:

    public static bool RetryDeleteFile(string filename, TimeSpan retryFor)
    {
        DateTime startTime = DateTime.Now;
        while (DateTime.Now - startTime <= retryFor)
        {
            Thread.Sleep(100); // wait a little
            try
            {
                File.Delete(filename);
                return true;
            }
            catch (UnauthorizedAccessException ex)
            {
            }
        }
        return false;
    }
    
    
    • Marked as answer by Samuel Stanojevic Friday, January 8, 2010 4:20 PM
    • Edited by Tergiver Friday, January 8, 2010 4:20 PM changed to bool, infinite loop fix
    Friday, January 8, 2010 4:15 PM