Search code examples
c#.netfile-iofilesystemwatcher

Why won't the Windows ERASE command register an event on the FileSystemWatcher under Windows Server?


I have a program that watches for the creation and deletion of specific files in order to perform specific actions. We use the .NET FileSystemWatcher to monitor activity around these specific files. This program runs as a Windows service on .NET 3.5 and is coded in C#. We have come across an issue where using the Windows DEL command or the ERASE command to delete the file being watched does not fire any of the FileSystemWatcher events (changed, deleted, created, error, renamed). This only seems to be a problem on my Windows 2003 and Windows 2008 boxes, but not on my Windows 7 box. It works fine on Windows 7. I Googled extensively for this and have reviewed several StackOverflow posts with no luck. I have played with different configurations of the FileSystemWatcher - increased the buffer, tried various combinations of the NotifyFilter enum, turned on IncludeSubdirectories, increased and decreased permissions for the users involved in modifying the files and running the programs - all with no success. I put a break point at the top of my event handler, and it never hits on a DEL or an ERASE. It does work on a create and on a rename (REN). As a workaround, we are just renaming the file before we delete it. But I would like to know if there is a solution for this. Either some way I can code for it, or is there some configuration I can change on the Windows boxes that will cause the service to pick up the DEL or ERASE. Here is the code for my file watcher (comments removed for brevity):

using System;
using System.IO;
using System.Runtime.Serialization;

namespace GatewayEDI.ClaimRouter.FileManagement
{
    public delegate void FileChangedEventHandler(object sender, FileChangedEventArgs e);

    public enum Status
    {
        Added,
        Changed,
        Deleted,
        Unknown
    }

    public class FolderWatcher : IDisposable
    {
        protected FileSystemWatcher Watcher;

        public Status FileStatus
        {
            get;
            set;
        }

        public event FileChangedEventHandler FileChanged;

        public virtual void OnHasFileChanged(FileChangedEventArgs e)
        {
            FileChangedEventHandler fileChanged = FileChanged;
            if (fileChanged != null)
            {
                fileChanged(this, e);
            }
        }

        public FolderWatcher(string path, string file)
        {
            FileStatus = Status.Unknown;
            if (!string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(file))
                CreateFileSystemWatcher(path, file);
        }

        private void CreateFileSystemWatcher(string path, string file)
        {
            Watcher = new FileSystemWatcher();

            try
            {
                if (path.LastIndexOf("\\", StringComparison.CurrentCulture) < path.Length - 1)
                    path += "\\";

                Watcher.Path = path;
                Watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess | NotifyFilters.LastWrite;
                Watcher.Filter = file;

                Watcher.Created += WatcherChanged;
                Watcher.Changed += WatcherChanged;
                Watcher.Renamed += WatcherChanged;
                Watcher.Deleted += WatcherChanged;
                Watcher.Error += WatcherError;
                Watcher.EnableRaisingEvents = true;
            }
            catch (Exception ex)
            {
                FileStatus = Status.Unknown;
                throw new FolderWatcherException("Unexpected exception searching file.  Path = " + Watcher.Path + ".  Error = " + ex.Message, ex);
            }
        }

        void WatcherError(object sender, ErrorEventArgs e)
        {
            FileStatus = Status.Unknown;
            SendFileChangedEvent();
        }

        void WatcherChanged(object sender, FileSystemEventArgs e)
        {
            switch (e.ChangeType)
            {
                case WatcherChangeTypes.Created:
                    FileStatus = Status.Added;
                    break;
                case WatcherChangeTypes.Changed:
                    FileStatus = Status.Changed;
                    break;
                case WatcherChangeTypes.Renamed:
                    FileStatus = e.Name.Equals(Watcher.Filter) ? Status.Added : Status.Deleted;
                    break;
                case WatcherChangeTypes.Deleted:
                    FileStatus = Status.Deleted;
                    break;
                default:
                    FileStatus = Status.Unknown;
                    break;
            }

            SendFileChangedEvent();
        }

        protected void SendFileChangedEvent()
        {
            string fileName = Watcher == null ? string.Empty : Watcher.Filter;
            FileChangedEventArgs args = new FileChangedEventArgs
            {
                FileStatus = FileStatus,
                FileName = fileName
            };
            OnHasFileChanged(args);
        }

        public void DeleteFile()
        {
            FileInfo fi = new FileInfo(Watcher.Path + Watcher.Filter);
            try
            {
                fi.Delete();
            }
            catch (Exception ex)
            {
                throw new FolderWatcherException("Error deleting file.  Path = " + Watcher.Path + ", File = " + Watcher.Filter + ".  Error = " + ex.Message, ex);
            }
        }

        private bool _alreadyDisposed;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool isDisposing)
        {
            if (_alreadyDisposed)
                return;
            if (isDisposing)
            {
                if (Watcher != null)
                    Watcher.Dispose();
            }
            _alreadyDisposed = true;
        }

        ~FolderWatcher()
        {
            Dispose(false);
        }
    }

    [Serializable]
    public class FolderWatcherException : Exception
    {
        public FolderWatcherException()
        {
        }

        public FolderWatcherException(string message) : base(message)
        {
        }

        public FolderWatcherException(string message, Exception inner) : base(message, inner)
        {
        }

        protected FolderWatcherException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

The Windows commands I use to modify the files are:

Create:  echo creating main flag > Main.flg
Delete:  DEL Main.flg
Rename:  REN Main.flg Main1.flg

I realize that FileSystemWatcher can be somewhat unreliable, but in this case I can consistently reproduce the issue. At some point, when time allows, we will replace this file watching component with something a little more stable.


Solution

  • I wrote a small program to see if I could reproduce this issue on my own Windows Server 2008 R2 box. Here's my code:

    using System;
    using System.IO;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                FileSystemWatcher fsw = new FileSystemWatcher();
                fsw.Path = @"D:\test";
                fsw.Deleted += new FileSystemEventHandler(ReportChange); 
                fsw.Created += new FileSystemEventHandler(ReportChange);
                fsw.EnableRaisingEvents = true;
    
                Console.In.ReadLine();
            }
    
            private static void ReportChange(object source, FileSystemEventArgs e)
            {
                // Specify what is done when a file is created or deleted.
                Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
            }
        }
    }
    

    My code above successfully reported file deletions from the directory I was monitoring.

    In looking closer at your code, it looks like your telling the FSW exactly what file to monitor. To me, this looks like a timing issue. Based on the code you've pasted, I can't tell how the creation of the FSW fits into the process of the file being deleted. Are you certain that the FSW is being created, initialized and enabled before the file that you want to watch is deleted? My suspicion is that the file is being deleted first, and then the FSW is being created to monitor for a specific file that doesn't actually exist anymore.