none
Is this the best way to recover from a transitory FileSystemWatcher error event? RRS feed

  • Question

  • I have FSW process monitoring a directory and occasionally the FSW Error event fires because of a "The specified network name is no longer available" error. I think the error is a transitory one resulting from occasional network issues. However, once the error event is fired, the FSW stops processing.   Since the error is a transitory one, I'd like to check for the error and have the FSW start processing after waiting a few seconds.  I tried simply resetting the EnablingRaiseEvents to true but that didn't seem to do anything.  I found the below code online which seems to do what I want but before using it, I wanted to post here to find out if this is the best way to approach this issue or if there is a better way.  Please advise.

    fileSystemWatcher.Error += new ErrorEventHandler(OnError);
    
    
    private void OnError(object source, ErrorEventArgs e)
    {
       NotAccessibleError(fileSystemWatcher ,e);
    }
    
    
    
    void NotAccessibleError(FileSystemWatcher source, ErrorEventArgs e)
    {
        int iMaxAttempts = 120;
        int iTimeOut = 30000;
        int i = 0;
        while ((!Directory.Exists(source.Path) || source.EnableRaisingEvents == false) && i < iMaxAttempts)
        {
            i += 1;
            try
            {
                source.EnableRaisingEvents = false;
                if (!Directory.Exists(source.Path))
                {
                    MyEventLog.WriteEntry("Directory Inaccessible " + source.Path + " at " + DateTime.Now.ToString("HH:mm:ss"));
                    System.Threading.Thread.Sleep(iTimeOut);
                }
                else
                { 
                    // ReInitialize the Component
                    source.Dispose();
                    source = null;
                    source = new System.IO.FileSystemWatcher();
                    ((System.ComponentModel.ISupportInitialize)(source)).BeginInit();
                    source.EnableRaisingEvents = true;
                    source.Filter = "*.tif";
                    source.Path = @"\\server\dir";
                    source.NotifyFilter = System.IO.NotifyFilters.FileName;
                    source.Created += new System.IO.FileSystemEventHandler(fswCatchImages_Changed);
                    source.Renamed += new System.IO.RenamedEventHandler(fswCatchImages_Renamed);
                    source.Error += new ErrorEventHandler(OnError);
                    ((System.ComponentModel.ISupportInitialize)(source)).EndInit();
                    MyEventLog.WriteEntry("Try to Restart RaisingEvents Watcher at " + DateTime.Now.ToString("HH:mm:ss"));
                }
            }
            catch (Exception error)
            {
                MyEventLog.WriteEntry("Error trying Restart Service " + error.StackTrace + " at " + DateTime.Now.ToString("HH:mm:ss"));
                source.EnableRaisingEvents = false;
                System.Threading.Thread.Sleep(iTimeOut);
            }
        }
    }
    
    


    Friday, December 1, 2017 11:33 AM

Answers

  • Yes, something like that should be fine.

    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by rsine Monday, December 4, 2017 11:10 AM
    Saturday, December 2, 2017 3:35 AM
    Moderator
  • Michael,

    I see now I misread your early response and you think I should do something like this:

    System.Timers.Timer timer = new System.Timers.Timer(30000);
    
    timer.Elapsed += Timer_Elapsed;
    
    
    private void OnWatcherError(object source, ErrorEventArgs e)
    {
      LogError(e.GetException());
    
      fileMonitor.EnableRaisingEvents = false;
      timer.Enabled = true;
      
    } 
    
    private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
       fileMonitor.EnableRaisingEvents = true;
       ((System.Timers.Timer)sender).Enabled = false;
    }

    Friday, December 1, 2017 5:37 PM

All replies

  • I don't know that I've ever seen the FSW stop processing events because of errors. It will stop reporting errors if the error buffer fills up because you're not processing. It could also occur if the path became "removed". But the code you posted is using Directory.Exists and that call would fail if it wasn't there.

    Also note that your catch code isn't going to work properly. The error event is technically raised on a worker thread that the OS is using for file system notifications. As documented in MSDN, your handlers cannot block this thread. If they block the thread you'll stall the file system. So you don't want to be blocking inside that code nor do you want to be sleeping. It will basically break the FSW code which may be the problem you're seeing.

    Personally, I would recommend that you simply report the error and continue on. You shouldn't need to do anything in most cases. If you really want to handle a network disconnect then you'll need to do so using a secondary thread. When you detect this issue in your error handler, queue a worker item to check back later for the situation. Once the situation has been resolved, then resume listening again. Don't try to do this on the FSW error thread.

    To queue an item you can do a couple of different things. A retry timer in your code would work. Alternately you could queue an item in the thread pool. 

    To reset the FSW you should be able to disable raising events (which disconnects everything) and then enable them again. This should trigger a reset, although I haven't verified this in a very long time.


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, December 1, 2017 3:52 PM
    Moderator
  • Michael,

    I think the directory the FSW is monitoring is temporarily not there hence the error "The specified network name is no longer available".  It's not that its removed but network issues are preventing it being seen by the FSW.  This error appears to stop all processing of documents which started me hunting down a solution.  The code I posted was from another person who seemed to be having the same issue.  While the poster of that code stated it worked to resolve his issue, I wanted to post here before using as I thought there might be a better way.  If I'm understanding you correctly, this code should not be used because of the sleep blocking the thread and the better option is to simply have something like the following in the error event handler:

    private void OnWatcherError(object source, ErrorEventArgs e)
    {
    LogError(e.GetException());
    
    if(!fileMonitor.EnableRaisingEvents)
    {
    fileMonitor.EnableRaisingEvents = true;
    }
    }

    Is my understanding correct?

    Friday, December 1, 2017 4:27 PM
  • The if statement will never be true by default. The enable flag is set otherwise you wouldn't get the event. After logging the error you should schedule a thread pool thread or timer to enable the FSW later after you've waited whatever amount of time is appropriate for your scenario.

    Michael Taylor http://www.michaeltaylorp3.net

    Friday, December 1, 2017 4:46 PM
    Moderator
  • Michael,

    I see now I misread your early response and you think I should do something like this:

    System.Timers.Timer timer = new System.Timers.Timer(30000);
    
    timer.Elapsed += Timer_Elapsed;
    
    
    private void OnWatcherError(object source, ErrorEventArgs e)
    {
      LogError(e.GetException());
    
      fileMonitor.EnableRaisingEvents = false;
      timer.Enabled = true;
      
    } 
    
    private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
       fileMonitor.EnableRaisingEvents = true;
       ((System.Timers.Timer)sender).Enabled = false;
    }

    Friday, December 1, 2017 5:37 PM
  • Yes, something like that should be fine.

    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by rsine Monday, December 4, 2017 11:10 AM
    Saturday, December 2, 2017 3:35 AM
    Moderator