Search code examples
c#.netfilesystemwatcher

How to start the c# FileSystemWatcher function indefinitely when executing the program?


I am developing windows program using .Net Framework.

I want to create a program that executes a function when a file is created in a specific folder using FileSystemWatcher.

Below is my code.

public async Task<int> CollectFunc() {
   string path = @"C:\test";
   try
   {
      FileSystemWatcher watcher = new FileSystemWatcher
      {
      Path=path
      Filter="test.log"
      };
      watcher.Created += new FileSystemEventHandler(WatcherFunc);
      watcher.IncludeSubdrectories=true;
      watcher.EnableRaisingEvents=true;
   }
   catch
   {
      Console.WriteLine("Error");
   }
   

   while(true)
   {
      await Task.Delay(100000);
   }
}

public async void WatcherFunc(object source, FileSystemEventArgs e) {
   Console.WriteLine("File Created: " + e.FullPath);
}

When I start the program, file creation is monitored until I close the program.

An example is shown below.

On September 1st, the following file is created. C:\test\20200901\test.log The program then prints "File Created: C:\test\20200901\test.log".

And on September 2nd C:\test\20200902\test.log file is created, The program will then output "File Created: C:\test\20200902\test.log".

...

But sometimes the Watcher doesn't work and I have to reboot the program.

Please let me know if there is any better or more stable logic than my source code.

I look forward to your kind reply.


Solution

  • Try these changes:

    // Introduce a class field, to prevent the watcher reference from going out of scope.
    private FileSystemWatcher watcher = null;
    
    public void CollectFunc() { // no need for async any more ...
       string path = @"C:\test";
       try
       {
          // Init class field
          watcher = new FileSystemWatcher
          {
          Path=path
          Filter="test.log"
          };
          watcher.Created += new FileSystemEventHandler(WatcherFunc);
          watcher.IncludeSubdrectories=true;
          watcher.EnableRaisingEvents=true;
       }
       catch (Exception ex)
       {
          // Better know what the problem actually was.
          Console.WriteLine($"Error: {ex.Message}");
       }
       
       // It's a winforms app - we don't need to block this => away with while(true)
    }
    
    public async void WatcherFunc(object source, FileSystemEventArgs e) 
    {
       // Just in case, catch and log exceptions
       try{
           Console.WriteLine("File Created: " + e.FullPath);
       } catch( Exception ex ) {
          // TODO: Log Exception or handle it.
       }
    }
    

    On top of that: It is a known issue, that a high number and frequency of changes can lead to some buffer to overflow in the watcher (if that still applies, but I remember running into this some years ago).

    The problem with buffer overflow is mentioned here : https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.internalbuffersize?view=netcore-3.1#remarks

    It may also be worthwhile to register a handler to the Error event: https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.error?view=netcore-3.1

    I guess that your Console.WriteLine in the event handler is just an example code and you are actually doing more than that. In the past, I found that it releaves stress from the FileSystemWatcher's buffer if I keep code very small here and handle the event as quickly as possible.

    So, what I did was enqueue the file path in a qeue and have that queue handled on a different thread. This ensures that event are handled as quickly as possible while not losing any. Peeks can be caught by the queue getting bigger and be dealt with independently by another thread. In other words: Things pile up outside the watcher's buffers.