Search code examples
c#filesystemwatcher

C# : FileSystemWatcher - multiple watching folders issue


Im trying write program in c# which is watching multiple folders. If in any of watched folders are added file, program should create copy in defined paths. My problem is when i create file, program create copies in wrong folder

e.g. if i add file into

 C:\folder1\stuff\ 
it should create copy in
 D:\stuff1\copied1...3\
but instead it created copies in
 D:\stuff2\copied1...3\

There is code :

namespace Watcher
{
    public class Watcher
    {

        struct Paths
        {
            public string sourcePath;
            public List<string> destinations;
            public FileSystemWatcher Watcher;
        }

        List<Paths> paths = new List<Paths>();

        public Watcher()
        {
            createWatchTower();
        }

        public void watch()
        {
            foreach (Paths p in paths)
            {
                p.Watcher.Created += (sender, e) => onCreate(sender, e, p.destinations);
            }
        }

        void createWatchTower()
        {
            Paths p1;
            p1.destinations = new List<string>();

            p1.sourcePath = @"C:\folder1\stuff\";

            p1.Watcher = new FileSystemWatcher();
            p1.Watcher.Path = p1.sourcePath;
            p1.Watcher.EnableRaisingEvents = true;

            p1.destinations.Add(@"D:\stuff1\copied1\");
            p1.destinations.Add(@"D:\stuff1\copied2\");
            p1.destinations.Add(@"D:\stuff1\copied3\");
            paths.Add(p1);


            Paths p2;
            p2.destinations = new List<string>();
            p2.sourcePath = @"C:\folder2\stuff2";

            p2.Watcher = new FileSystemWatcher();
            p2.Watcher.Path = p2.sourcePath;
            p2.Watcher.EnableRaisingEvents = true;

            p2.destinations.Add(@"D:\stuff2\copied1\");
            p2.destinations.Add(@"D:\stuff2\copied2\");
            p2.destinations.Add(@"D:\stuff2\copied3\");

            paths.Add(p2);

        }

        private void onCreate(object o, FileSystemEventArgs e, List<string> dest)
        {

            foreach (string s in dest)
            {
                try
                {
                    System.IO.File.Copy(e.FullPath, s + e.Name, true);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
        }
    }
}

Could anyone help me ? I think its because event in foreach, but i cant find sollution for this. Thanks much post


Solution

  • If you are not using C# 5 then the problem is the closure on p in the foreach-loop in the watch method:

    foreach (Paths p in paths)
    {
        p.Watcher.Created += (sender, e) => onCreate(sender, e, p.destinations);
    }
    

    When using p inside the lambda it will capture the variable, not its value. So if the Created event is raised p will refer to the last item of the paths list.

    You can avoid that by introducing a temporary variable inside the foreach-loop:

    foreach (Paths p in paths)
    {
        var tmp = p;
        p.Watcher.Created += (sender, e) => onCreate(sender, e, tmp.destinations);
    }
    

    You can find a more detailed analysis of what changed in C# 5 in this Stackoverflow question.