FileSystemWatcher Changed event is raised twice

前端 未结 30 3196
死守一世寂寞
死守一世寂寞 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:10

    Any duplicated OnChanged events from the FileSystemWatcher can be detected and discarded by checking the File.GetLastWriteTime timestamp on the file in question. Like so:

    DateTime lastRead = DateTime.MinValue;
    
    void OnChanged(object source, FileSystemEventArgs a)
    {
        DateTime lastWriteTime = File.GetLastWriteTime(uri);
        if (lastWriteTime != lastRead)
        {
            doStuff();
            lastRead = lastWriteTime;
        }
        // else discard the (duplicated) OnChanged event
    }
    
    0 讨论(0)
  • 2020-11-22 06:10

    Here is my solution which helped me to stop the event being raised twice:

    watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
    

    Here I have set the NotifyFilter property with only Filename and size.
    watcher is my object of FileSystemWatcher. Hope this will help.

    0 讨论(0)
  • 2020-11-22 06:11

    I had to combine several ideas from the posts above and add file locking check to get it working for me:

    FileSystemWatcher fileSystemWatcher;
    
    private void DirectoryWatcher_Start()
    {
        FileSystemWatcher fileSystemWatcher = new FileSystemWatcher
        {
            Path = @"c:\mypath",
            NotifyFilter = NotifyFilters.LastWrite,
            Filter = "*.*",
            EnableRaisingEvents = true
        };
    
        fileSystemWatcher.Changed += new FileSystemEventHandler(DirectoryWatcher_OnChanged);
    }
    
    private static void WaitUntilFileIsUnlocked(String fullPath, Action<String> callback, FileAccess fileAccess = FileAccess.Read, Int32 timeoutMS = 10000)
    {
        Int32 waitMS = 250;
        Int32 currentMS = 0;
        FileInfo file = new FileInfo(fullPath);
        FileStream stream = null;
        do
        {
            try
            {
                stream = file.Open(FileMode.Open, fileAccess, FileShare.None);
                stream.Close();
                callback(fullPath);
                return;
            }
            catch (IOException)
            {
            }
            finally
            {
                if (stream != null)
                    stream.Dispose();
            }
            Thread.Sleep(waitMS);
            currentMS += waitMS;
        } while (currentMS < timeoutMS);
    }    
    
    private static Dictionary<String, DateTime> DirectoryWatcher_fileLastWriteTimeCache = new Dictionary<String, DateTime>();
    
    private void DirectoryWatcher_OnChanged(Object source, FileSystemEventArgs ev)
    {
        try
        {
            lock (DirectoryWatcher_fileLastWriteTimeCache)
            {
                DateTime lastWriteTime = File.GetLastWriteTime(ev.FullPath);
                if (DirectoryWatcher_fileLastWriteTimeCache.ContainsKey(ev.FullPath))
                {
                    if (DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath].AddMilliseconds(500) >= lastWriteTime)
                        return;     // file was already handled
                }
    
                DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath] = lastWriteTime;
            }
    
            Task.Run(() => WaitUntilFileIsUnlocked(ev.FullPath, fullPath =>
            {
                // do the job with fullPath...
            }));
    
        }
        catch (Exception e)
        {
            // handle exception
        }
    }
    
    0 讨论(0)
  • 2020-11-22 06:11

    I approached the double create issue like this, which ignores the first event:

    Private WithEvents fsw As New System.IO.FileSystemWatcher
    Private complete As New List(Of String)
    
    Private Sub fsw_Created(ByVal sender As Object, _
        ByVal e As System.IO.FileSystemEventArgs) Handles fsw.Created
    
        If Not complete.Contains(e.FullPath) Then
            complete.Add(e.FullPath)
    
        Else
            complete.Remove(e.FullPath)
            Dim th As New Threading.Thread(AddressOf hprocess)
            th.Start(e)
    
        End If
    
    End Sub
    
    0 讨论(0)
  • 2020-11-22 06:13

    I spent some significant amount of time using the FileSystemWatcher, and some of the approaches here will not work. I really liked the disabling events approach, but unfortunately, it doesn't work if there is >1 file being dropped, second file will be missed most if not all times. So I use the following approach:

    private void EventCallback(object sender, FileSystemEventArgs e)
    {
        var fileName = e.FullPath;
    
        if (!File.Exists(fileName))
        {
            // We've dealt with the file, this is just supressing further events.
            return;
        }
    
        // File exists, so move it to a working directory. 
        File.Move(fileName, [working directory]);
    
        // Kick-off whatever processing is required.
    }
    
    0 讨论(0)
  • 2020-11-22 06:13

    You could try to open it for write, and if successful then you could assume the other application is done with the file.

    private void OnChanged(object source, FileSystemEventArgs e)
    {
        try
        {
            using (var fs = File.OpenWrite(e.FullPath))
            {
            }
            //do your stuff
        }
        catch (Exception)
        {
            //no write access, other app not done
        }
    }
    

    Just opening it for write appears not to raise the changed event. So it should be safe.

    0 讨论(0)
提交回复
热议问题