FileSystemWatcher Changed event is raised twice

前端 未结 30 3198
死守一世寂寞
死守一世寂寞 2020-11-22 06:01

I have an application where I am looking for a text file and if there are any changes made to the file I am using the OnChanged eventhandler to handle the event

相关标签:
30条回答
  • 2020-11-22 06:26

    mostly for future me :)

    I wrote a wrapper using Rx:

     public class WatcherWrapper : IDisposable
    {
        private readonly FileSystemWatcher _fileWatcher;
        private readonly Subject<FileSystemEventArgs> _infoSubject;
        private Subject<FileSystemEventArgs> _eventSubject;
    
        public WatcherWrapper(string path, string nameFilter = "*.*", NotifyFilters? notifyFilters = null)
        {
            _fileWatcher = new FileSystemWatcher(path, nameFilter);
    
            if (notifyFilters != null)
            {
                _fileWatcher.NotifyFilter = notifyFilters.Value;
            }
    
            _infoSubject = new Subject<FileSystemEventArgs>();
            _eventSubject = new Subject<FileSystemEventArgs>();
    
            Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Changed").Select(e => e.EventArgs)
                .Subscribe(_infoSubject.OnNext);
            Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Created").Select(e => e.EventArgs)
                .Subscribe(_infoSubject.OnNext);
            Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Deleted").Select(e => e.EventArgs)
                .Subscribe(_infoSubject.OnNext);
            Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Renamed").Select(e => e.EventArgs)
                .Subscribe(_infoSubject.OnNext);
    
            // this takes care of double events and still works with changing the name of the same file after a while
            _infoSubject.Buffer(TimeSpan.FromMilliseconds(20))
                .Select(x => x.GroupBy(z => z.FullPath).Select(z => z.LastOrDefault()).Subscribe(
                    infos =>
                    {
                        if (infos != null)
                            foreach (var info in infos)
                            {
                                {
                                    _eventSubject.OnNext(info);
                                }
                            }
                    });
    
            _fileWatcher.EnableRaisingEvents = true;
        }
    
        public IObservable<FileSystemEventArgs> FileEvents => _eventSubject;
    
    
        public void Dispose()
        {
            _fileWatcher?.Dispose();
            _eventSubject.Dispose();
            _infoSubject.Dispose();
        }
    }
    

    Usage:

    var watcher = new WatcherWrapper(_path, "*.info");
    // all more complicated and scenario specific filtering of events can be done here    
    watcher.FileEvents.Where(x => x.ChangeType != WatcherChangeTypes.Deleted).Subscribe(x => //do stuff)
    
    0 讨论(0)
  • 2020-11-22 06:27

    Try this, It's working fine

      private static readonly FileSystemWatcher Watcher = new FileSystemWatcher();
        static void Main(string[] args)
        {
            Console.WriteLine("Watching....");
    
            Watcher.Path = @"D:\Temp\Watcher";
            Watcher.Changed += OnChanged;
            Watcher.EnableRaisingEvents = true;
            Console.ReadKey();
        }
    
        static void OnChanged(object sender, FileSystemEventArgs e)
        {
            try
            {
                Watcher.Changed -= OnChanged;
                Watcher.EnableRaisingEvents = false;
                Console.WriteLine($"File Changed. Name: {e.Name}");
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
            finally
            {
                Watcher.Changed += OnChanged;
                Watcher.EnableRaisingEvents = true;
            }
        }
    
    0 讨论(0)
  • 2020-11-22 06:28

    I've "fixed" that problem using the following strategy in my delegate:

    // fsw_ is the FileSystemWatcher instance used by my application.
    
    private void OnDirectoryChanged(...)
    {
       try
       {
          fsw_.EnableRaisingEvents = false;
    
          /* do my stuff once asynchronously */
       }
    
       finally
       {
          fsw_.EnableRaisingEvents = true;
       }
    }
    
    0 讨论(0)
  • 2020-11-22 06:28

    In my case need to get the last line of a text file that is inserted by other application, as soon as insertion is done. Here is my solution. When the first event is raised, i disable the watcher from raising others, then i call the timer TimeElapsedEvent because when my handle function OnChanged is called i need the size of the text file, but the size at that time is not the actual size, it is the size of the file imediatelly before the insertion. So i wait for a while to proceed with the right file size.

    private FileSystemWatcher watcher = new FileSystemWatcher();
    ...
    watcher.Path = "E:\\data";
    watcher.NotifyFilter = NotifyFilters.LastWrite ;
    watcher.Filter = "data.txt";
    watcher.Changed += new FileSystemEventHandler(OnChanged);
    watcher.EnableRaisingEvents = true;
    
    ...
    
    private void OnChanged(object source, FileSystemEventArgs e)
       {
        System.Timers.Timer t = new System.Timers.Timer();
        try
        {
            watcher.Changed -= new FileSystemEventHandler(OnChanged);
            watcher.EnableRaisingEvents = false;
    
            t.Interval = 500;
            t.Elapsed += (sender, args) => t_Elapsed(sender, e);
            t.Start();
        }
        catch(Exception ex) {
            ;
        }
    }
    
    private void t_Elapsed(object sender, FileSystemEventArgs e) 
       {
        ((System.Timers.Timer)sender).Stop();
           //.. Do you stuff HERE ..
         watcher.Changed += new FileSystemEventHandler(OnChanged);
         watcher.EnableRaisingEvents = true;
    }
    
    0 讨论(0)
  • 2020-11-22 06:29

    Try with this code:

    class WatchPlotDirectory
    {
        bool let = false;
        FileSystemWatcher watcher;
        string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";
    
        public WatchPlotDirectory()
        {
            watcher = new FileSystemWatcher();
            watcher.Path = path;
            watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                                   | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            watcher.Filter = "*.*";
            watcher.Changed += new FileSystemEventHandler(OnChanged);
            watcher.Renamed += new RenamedEventHandler(OnRenamed);
            watcher.EnableRaisingEvents = true;
        }
    
    
    
        void OnChanged(object sender, FileSystemEventArgs e)
        {
            if (let==false) {
                string mgs = string.Format("File {0} | {1}",
                                           e.FullPath, e.ChangeType);
                Console.WriteLine("onchange: " + mgs);
                let = true;
            }
    
            else
            {
                let = false;
            }
    
    
        }
    
        void OnRenamed(object sender, RenamedEventArgs e)
        {
            string log = string.Format("{0} | Renamed from {1}",
                                       e.FullPath, e.OldName);
            Console.WriteLine("onrenamed: " + log);
    
        }
    
        public void setPath(string path)
        {
            this.path = path;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 06:30

    Sorry for the grave dig, but I've been battling this issue for a while now and finally came up with a way to handle these multiple fired events. I would like to thank everyone in this thread as I have used it in many references when battling this issue.

    Here is my complete code. It uses a dictionary to track the date and time of the last write of the file. It compares that value, and if it is the same, it suppresses the events. It then sets the value after starting the new thread.

    using System.Threading; // used for backgroundworker
    using System.Diagnostics; // used for file information
    private static IDictionary<string, string> fileModifiedTable = new Dictionary<string, string>(); // used to keep track of our changed events
    
    private void fswFileWatch_Changed( object sender, FileSystemEventArgs e )
        {
            try
            {
               //check if we already have this value in our dictionary.
                if ( fileModifiedTable.TryGetValue( e.FullPath, out sEmpty ) )
                {              
                    //compare timestamps      
                    if ( fileModifiedTable[ e.FullPath ] != File.GetLastWriteTime( e.FullPath ).ToString() )
                    {        
                        //lock the table                
                        lock ( fileModifiedTable )
                        {
                            //make sure our file is still valid
                            if ( File.Exists( e.FullPath ) )
                            {                               
                                // create a new background worker to do our task while the main thread stays awake. Also give it do work and work completed handlers
                                BackgroundWorker newThreadWork = new BackgroundWorker();
                                newThreadWork.DoWork += new DoWorkEventHandler( bgwNewThread_DoWork );
                                newThreadWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bgwNewThread_RunWorkerCompleted );
    
                                // capture the path
                                string eventFilePath = e.FullPath;
                                List<object> arguments = new List<object>();
    
                                // add arguments to pass to the background worker
                                arguments.Add( eventFilePath );
                                arguments.Add( newEvent.File_Modified );
    
                                // start the new thread with the arguments
                                newThreadWork.RunWorkerAsync( arguments );
    
                                fileModifiedTable[ e.FullPath ] = File.GetLastWriteTime( e.FullPath ).ToString(); //update the modified table with the new timestamp of the file.
                                FILE_MODIFIED_FLAG.WaitOne(); // wait for the modified thread to complete before firing the next thread in the event multiple threads are being worked on.
                            }
                        }
                    }
                }
            }
            catch ( IOException IOExcept )
            {
                //catch any errors
                postError( IOExcept, "fswFileWatch_Changed" );
            }
        }
    
    0 讨论(0)
提交回复
热议问题