7

FileSystemWatcher does not work properly when many files are added to the directory at the same time...

The Watcher simply doesn't find all the files in the directory - only if the files are placed in the folder one by one - not if lots of files are copied to the folder at the same time...

Is the creation of Threads the solution to the problem or is there another way to handle the problem ?

Henrik
  • 71
  • 1
  • 3
  • 1
    Keep in mind the FileSystemWatcher is relying on the OS to signal when files are added/modified/deleted/etc. So having multiple instances isn't going to help. – Agent_9191 Oct 15 '09 at 13:46

3 Answers3

12

The documentation on that class details that problem:

The Windows operating system notifies your component of file changes in a buffer created by the FileSystemWatcher. If there are many changes in a short time, the buffer can overflow. This causes the component to lose track of changes in the directory, and it will only provide blanket notification. Increasing the size of the buffer with the InternalBufferSize property is expensive, as it comes from non-paged memory that cannot be swapped out to disk, so keep the buffer as small yet large enough to not miss any file change events. To avoid a buffer overflow, use the NotifyFilter and IncludeSubdirectories properties so you can filter out unwanted change notifications.

So, threads probably won't help you much in this case. You probably want to either increase the buffer size (but how large it should be may well depend on the speed of the computer and the disk itself) or constrain what files you are interested in by setting the appropriate filter.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • 1
    And, of course, another option is to additionally poll the folder for new files and files that were missed out. – Dirk Vollmar Oct 15 '09 at 14:05
  • I sometimes underestimate C#, .Net etc. since development in Microsoft world is easy (by saying "Microsoft should have think that for me too...") and behave cocky so don't read documentation and then put hours into small things like this one lol – yakya Feb 01 '17 at 13:50
  • @DirkVollmar How would you do that? Can you artificially re-raise events for files that already residing in the folder? – Frying Pan Apr 21 '21 at 21:21
1

C# is new to me and I struggled with the same trouble for nearly a week. I had this:

    private void btnWatchFile_Click(object sender, EventArgs e)
    {
        //code to create a watcher and allow it to reise events...
    }

    //watcher onCreate event
    public void onCreated(object sender, FileSystemEventArgs e)
    {
        if (!updateNotifications )
        {
            stringBuilder.Remove(0, stringBuilder.Length);
            stringBuilder.Append(e.FullPath);
            stringBuilder.Append(" ");
            stringBuilder.Append(e.ChangeType.ToString());
            stringBuilder.Append("    ");
            stringBuilder.Append(DateTime.Now.ToString());
            updateNotifications = true;
        }
    }

    //timer to check the flag every X time
    private void timer_Tick(object sender, EventArgs e)
    {
        if (updateNotifications )
        {
            notificationListBox.Items.Insert(0, stringBuilder.ToString());
            updateNotifications = false;
        }
    }

I even set the timer interval to 1 millisecond and yet some new file events were missing. I tried to update the notificationsListBox from inside the onCreated event but I always got a Cross-reference error. This was until I found out that the watcher onCreated event is executed in a thread other than the one of the main method thread, so, in a nut shell, this is my solution:

I included public delegate void Action() as an attribute of my class and then used Invoke to update the notificationsListBox from inside the onCreated event. Next, the piece code:

    public void onCreated(object sender, FileSystemEventArgs e)
    {
        stringBuilder.Remove(0, stringBuilder.Length);
        stringBuilder.Append(e.FullPath);
        stringBuilder.Append(" ");
        stringBuilder.Append(e.ChangeType.ToString());
        stringBuilder.Append("    ");
        stringBuilder.Append(DateTime.Now.ToString());
        updateNotifications = true;
        Invoke((Action)(() => {notificationListBox.Items.Insert(0, stringBuilder.ToString());}));
    }

So the timer and its code are no longer necessary. This works excellent for me and I hope it does for anyone with a similar situation. Best regards!!!

3sley
  • 11
  • 3
0

try something like this.

public MainWindow()
    {
        InitializeComponent();

        #region initialise FileSystemWatcher
        FileSystemWatcher watch = new FileSystemWatcher();
        watch.Path = folder;
        watch.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
        watch.Filter = ext;
        watch.Changed += new FileSystemEventHandler(OnChanged);
        watch.Created += new FileSystemEventHandler(OnChanged);
        watch.EnableRaisingEvents = true;
        #endregion

    }

 private void OnChanged(object source, FileSystemEventArgs e)
    {
        Application.Current.Dispatcher.BeginInvoke((Action)delegate
        {
          // do your work here. If this work needs more time than it can be processed, not the filesystembuffer overflows but your application will block. In this case try to improve performance here.
        }, System.Windows.Threading.DispatcherPriority.Normal);
    }
Andreas
  • 3,843
  • 3
  • 40
  • 53