Dominic B.
Dominic B.

Reputation: 1907

FileSystemWatcher - Recognize files in deleted folder

I have another problem with my FileSystemWatcher ;) Simply, what I want to do:

I want to watch specific folders, in order to add the paths of the files to a List(Of String) and set an integer-variable for each new file.

So:

I create the file "Test.png" and it does that:

(List) categoryFilesList.Add(e.FullPath)
(Integer-variable) counter += 1

Well, this is working well.

Now, if a file is deleted again, it should remove the path from the list and set the integer-variable down again. (-= 1).

This is working well, too, but now I have a problem, I am not able to find a solution for. I have IncludeSubdirectories set to true to scan the subdirectories for files. On adding this works fine, but on removing it does not.

That is because I do NOT add folders to the list, only files. So, if I delete a folder, I am not able to know what content it was containing and how much files it were, because it is already gone.

But I would want to know, how many files there were in and what their paths were to remove them again from the list and set the integer-variable down again with the amount of deleted files.

I though about saving it anyway on changing, but I do not know how exactly and if this is a good idea. Is there a better solution?

If something is unclear, then I am sorry, ask then. I also accept C#-answers, VB.NET is just because my friend wants that.

Thanks!

Upvotes: 4

Views: 1351

Answers (2)

steveg89
steveg89

Reputation: 1827

My solution to a similar problem is as follows: Ensure your FileSystemWatcher is configured to track subfolders. Once you do that, you should get a Delete event any time a folder or file is deleted. In the case where a folder is deleted, simply iterate through your collection and remove any items that contain the path of the folder.

For example:

int numRemoved = categoryFilesList.RemoveAll(
            delegate(String s)
            {
               return s.Contains(<DeletedDirectoryPath>);
            }
            );

This would remove any items in your list which contain . You can then use numRemoved to maintain your file count. Alternatively, you could simply use the count of the list instead of maintaining your own copy of that data via the Count property on your list.

Additionally you should be sure that you handle the cases where items and directories are renamed so that your list of filenames is always up to date.

Upvotes: 2

Tomas Pastircak
Tomas Pastircak

Reputation: 2857

I am afraid that this isn't really possible to do, because of the way the deletion of a directory works in a system. The directory is deleted, but the files recursively are just part of the directory.

This was quite well described here on MSDN forums :

I you observe the behavior of the trash on your desktop, you will see that each time you delete a folder, you can see that folder in the trash but you cannot see the elements within that dropped folder. The only way to see those elements is to recover the folder from the trash.

I think it happens the same thing with the FSW class. When you delete a folder inside a watched directory you only have the event of the deleted folder because the folder and its contents is not really deleted but only moved to the trash. This is why you never receive the deleted events for the included files because they are still somewhere on your system

According to this question here on SO, the same problem occurs when a folder is moved into the structure (and the question also shows the workaround to the moving issue).

Possible workaround for the deletion problem would be browsing the structure in advance and saving the amount of files in the directories into a tree-like structure, but it will definitely be much more complex than this. It would look like this:

public class DirectoryFiles
{
    public int Count {get; set;}

    public string FullPath {get; set;}
    public List<DirectoryFiles> Subdirectories {get; set; }
}

private DirectoryFiles Initialize(string fullPath)
{
    if (Directory.Exists(fullPath))
    {
        var toReturn = new DirectoryFiles { Subdirectories = new List<DirectoryFiles>() };

        foreach (string directory in Directory.GetDirectories(fullPath))
        {
            toReturn.Subdirectories.Add(this.Initialize(directory));
        }

        toReturn.Count = toReturn.Subdirectories.Sum(x => x.Count) + Directory.GetFiles(fullPath).Count();
        return toReturn;
    }
    else
    {
        throw new DirectoryNotFoundException(String.Format("Directory {0} does not exist", fullPath));
    }
}

and in the class where you are counting:

private int GetCountOfFiles(DirectoryFiles files, string fullPath)
{
    if (files.FullPath.Equals(fullPath, StringComparison.InvariantCultureIgnoreCase))
    {
        return files.Count;
    }
    foreach (var subdir in files.Subdirectories)
    {
        if (this.GetCountOfFiles(subdir, fullPath) != -1)
        {
            return subdir.Count;
        }
    }
    return -1;
}

This may need improvements for:

  • Permissions - it will throw an exception if you don't have access to the files,
  • The performance is not perfect, on every subfile or subdirectory deletion you'll need to rebuild the whole structure. There can be some optimizations created for that as well, but should work quite well for smaller subdirectories. If you need performance improvements, I'll leave that to you. For that, consider adding DirectoryFiles Parent to DirectoryFiles and recount the directories on the way up.

Upvotes: 2

Related Questions