28

I created a simple program to delete temporary files in C# (for fun, not a major project) and am running into locked files (in use) issues. How do you normally either exclude those files? For reference I am receiving the error:

The process cannot access the file 'ExchangePerflog_8484fa31c65c7a31cfcccd43.dat' because it is being used by another process.

Code:

static void Main(string[] args)
    {
        string folderPath = string.Empty;
        folderPath = System.Environment.GetEnvironmentVariable("temp");
        deleteFilesInDirectory(folderPath);
    }

    public static void deleteFilesInDirectory(string folderPath) 
    {

        try
        {
            var dir = new DirectoryInfo(folderPath);
            dir.Attributes = dir.Attributes & ~FileAttributes.ReadOnly;
            dir.Delete(true);
            MessageBox.Show(folderPath + " has been cleaned.");
        }
        catch (System.IO.IOException ex)
        {
            MessageBox.Show(ex.Message); 
            return;

        } 
    }     
Raj Ranjhan
  • 3,869
  • 2
  • 19
  • 29
Geekender
  • 799
  • 4
  • 9
  • 18
  • 1
    To clarify, do you want to skip over the files that are in use, or are you trying to force a deletion of them? – Tim May 08 '12 at 18:53
  • why not just collect undeleted files and show them after execution? Or you need to wait for them to get accessible? – cookieMonster May 08 '12 at 18:53
  • he said exclude so I think he means skip – Smash May 08 '12 at 18:54
  • This question is already answered, check this: http://stackoverflow.com/questions/6077869/movefile-function-in-c-sharp-delete-file-after-reboot-c-sharp – sergiogarciadev May 08 '12 at 19:00
  • There is also a utility called "Unlocker" - http://www.emptyloop.com/unlocker/ This can release the locks (as well as tell you who has them) which then allows you to delete them. More for investigation than general use, though. – dash May 08 '12 at 19:03
  • @Tim I would prefer to skip over them – Geekender May 08 '12 at 19:07
  • A better solution is to delete all temp files at startup from batch file started by scheduler. – i486 Feb 04 '16 at 08:48

6 Answers6

23

Description

There is no way to delete a file that is currently in use by another process. But you can wait till the file is not locked.

Check in a while loop till the file is unlocked with this method

protected virtual bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

Sample

FileInfo file = new FileInfo("PathToTheFile");
while (IsFileLocked(file))
    Thread.Sleep(1000);
file.Delete();

Update

If you want to skip locked files you can do this.

//
var dir = new DirectoryInfo(folderPath);
foreach(var file in dir.GetFiles()) {
    try
    {
        file.Delete();
    }
    catch (IOException)
    {
        //file is currently locked
    }
}
dknaack
  • 60,192
  • 27
  • 155
  • 202
  • If for example I am deleting all temp files while the machine is running, and I keep Outlook open, the temp files that Outlook has will not be deleted until Outlook is closed. Therefore the program will continue to try to execute until Outlook is closed using this method. I would prefer to check if it is in use and skip it if it is in use. – Geekender May 08 '12 at 19:11
  • The problem with this approach is that when `IsFileLocked` returns `false`, meaning that the file *was* not locked, it may end up being locked again for a brief period by the very code that has performed the verification. This can be easily observed on Windows. – BartoszKP Feb 16 '18 at 08:54
13

Try the following code. Just add two lines before file deletion:

GC.Collect();
GC.WaitForPendingFinalizers();
tumultous_rooster
  • 12,150
  • 32
  • 92
  • 149
Aravindhkumar
  • 205
  • 2
  • 2
  • 1
    This will have no effect for trying to delete files that are in use by another process. – crashmstr Feb 04 '16 at 08:11
  • This is nothing to do with file deletion. This just instructs the .Net framework to force Garbage collection. – Chris Feb 04 '16 at 08:29
  • 3
    Actually this is a perfectly valid comment if you use SQLite. See http://stackoverflow.com/questions/8511901/system-data-sqlite-close-not-releasing-database-file – Moeri Apr 06 '16 at 07:03
  • 2
    Wow, so many down votes on this. This indeed could be the reason you can not delete a file. In my case I had many threads working with a file (memory mapping). After Disposing of all my memory mapping threads and them cleaning themselves up, I was trying to delete the mapping file. It would not let me because the garbage collector did not have time to cleanup before the delete was happening. This fixed my issue, +1 from me! – Arvo Bowen Jan 29 '20 at 15:24
  • fixed it for mee too, though it is not the actual issue, IMHO many ppl landing here have the problem the file is blocked even after usage from another process. – c_ph_r Dec 09 '20 at 09:34
10

Well I have run into similar problems. When you try to remove directory shortly after removing file you have to force GC to release the file handle from current thread

public void DisposeAfterTest(string filePath)
    {

        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }

        GC.Collect();
        GC.WaitForPendingFinalizers();

        if (Directory.Exists(this.TempTestFolderPath))
        {
            Directory.Delete(this.TempTestFolderPath, true);
        }

    }
Bartosz Gawron
  • 189
  • 1
  • 8
  • 2
    This is a symptom of not wrapping all your file access classes in `using`. Dispose of file access handles correctly and you won't have this issue. – Liam Jan 07 '21 at 10:21
3

Using dknaack's code. This worked in my case.

FileInfo file = new FileInfo("xyz.txt");
try
{
    for (int tries = 0; IsFileLocked(file) && tries < 5; tries++)
        Thread.Sleep(1000);
    file.Delete();
}
catch (IOException exception)
{
    Console.WriteLine(string.Format("File locked: {0}", exception);
}
Martin
  • 3,396
  • 5
  • 41
  • 67
1

I don't believe there is any way you can know in advance if the file is in use or not. You could try to get an exclusive lock on the file; but then you'd just be trading one exception for another.

If these are files you are opening, see if you can't do a better job of closing them. If it's more complicated than that - you could maintain a 'to delete list' and continue to retry the delete until it is successful (on another thread with a concurrent collection maybe).

I also don't believe there is anyway to forcefully delete an in-use file.

Rob P.
  • 14,921
  • 14
  • 73
  • 109
0

The below code will delete the files from a directory and all its sub-directories excluding the locked files and gets the list of the files that are not deleted. You can change the SearchOption to TopDirectoryOnly if you consider only the current directory.

string []files = Directory.GetFiles(dirPath,"*.*", SearchOption.AllDirectories); //this gets the files in all subdirectories as well
List<string> lockedFiles = new List<string>();
foreach(string file in files) 
{    
    try    
    {        
        file.Delete();    
    }    
    catch (IOException)    
    {        
        lockedFiles.Add(file);    
    }
}
Chris
  • 113
  • 5