FileSystemWatcher Changed event is raised twice

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

    I have a very quick and simple workaround here, it does work for me, and no matter the event would be triggered once or twice or more times occasionally, check it out:

    private int fireCount = 0;
    private void inputFileWatcher_Changed(object sender, FileSystemEventArgs e)
        {
           fireCount++;
           if (fireCount == 1)
            {
                MessageBox.Show("Fired only once!!");
                dowork();
            }
            else
            {
                fireCount = 0;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 06:15

    My scenario is that I have a virtual machine with a Linux server in it. I am developing files on the Windows host. When I change something in a folder on the host I want all the changes to be uploaded, synced onto the virtual server via Ftp. This is how I do eliminate the duplicate change event when I write to a file ( which flags the folder containing the file to be modified as well ) :

    private Hashtable fileWriteTime = new Hashtable();
    
    private void fsw_sync_Changed(object source, FileSystemEventArgs e)
    {
        string path = e.FullPath.ToString();
        string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();
    
        // if there is no path info stored yet
        // or stored path has different time of write then the one now is inspected
        if ( !fileWriteTime.ContainsKey(path) ||
             fileWriteTime[path].ToString() != currentLastWriteTime
        )
        {
            //then we do the main thing
            log( "A CHANGE has occured with " + path );
    
            //lastly we update the last write time in the hashtable
            fileWriteTime[path] = currentLastWriteTime;
        }
    }
    

    Mainly I create a hashtable to store file write time information. Then if the hashtable has the filepath that is modified and it's time value is the same as the currently notified file's change then I know it is the duplicate of the event and ignore it.

    0 讨论(0)
  • 2020-11-22 06:15
    FileReadTime = DateTime.Now;
    
    private void File_Changed(object sender, FileSystemEventArgs e)
    {            
        var lastWriteTime = File.GetLastWriteTime(e.FullPath);
        if (lastWriteTime.Subtract(FileReadTime).Ticks > 0)
        {
            // code
            FileReadTime = DateTime.Now;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 06:17

    I have created a Git repo with a class that extends FileSystemWatcher to trigger the events only when copy is done. It discards all the changed events exept the last and it raise it only when the file become available for read.

    Download FileSystemSafeWatcher and add it to your project.

    Then use it as a normal FileSystemWatcher and monitor when the events are triggered.

    var fsw = new FileSystemSafeWatcher(file);
    fsw.EnableRaisingEvents = true;
    // Add event handlers here
    fsw.Created += fsw_Created;
    
    0 讨论(0)
  • 2020-11-22 06:19

    Here's my approach :

    // Consider having a List<String> named _changedFiles
    
    private void OnChanged(object source, FileSystemEventArgs e)
    {
        lock (_changedFiles)
        {
            if (_changedFiles.Contains(e.FullPath))
            {
                return;
            }
            _changedFiles.Add(e.FullPath);
        }
    
        // do your stuff
    
        System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
        timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
        {
            lock (_changedFiles)
            {
                _changedFiles.Remove(e.FullPath);
            }
        };
       timer.Start();
    }
    

    This is the solution I used to solve this issue on a project where I was sending the file as attachment in a mail. It will easily avoid the twice fired event even with a smaller timer interval but in my case 1000 was alright since I was happier with missing few changes than with flooding the mailbox with > 1 message per second. At least it works just fine in case several files are changed at the exact same time.

    Another solution I've thought of would be to replace the list with a dictionary mapping files to their respective MD5, so you wouldn't have to choose an arbitrary interval since you wouldn't have to delete the entry but update its value, and cancel your stuff if it hasn't changed. It has the downside of having a Dictionary growing in memory as files are monitored and eating more and more memory, but I've read somewhere that the amount of files monitored depends on the FSW's internal buffer, so maybe not that critical. Dunno how MD5 computing time would affect your code's performances either, careful =\

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

    I was able to do this by added a function that checks for duplicates in an buffer array.

    Then perform the action after the array has not been modified for X time using a timer: - Reset timer every time something is written to the buffer - Perform action on tick

    This also catches another duplication type. If you modify a file inside a folder, the folder also throws a Change event.

    Function is_duplicate(str1 As String) As Boolean
        If lb_actions_list.Items.Count = 0 Then
            Return False
        Else
            Dim compStr As String = lb_actions_list.Items(lb_actions_list.Items.Count - 1).ToString
            compStr = compStr.Substring(compStr.IndexOf("-") + 1).Trim
    
            If compStr <> str1 AndAlso compStr.parentDir <> str1 & "\" Then
                Return False
            Else
                Return True
            End If
        End If
    End Function
    
    Public Module extentions
    <Extension()>
    Public Function parentDir(ByVal aString As String) As String
        Return aString.Substring(0, CInt(InStrRev(aString, "\", aString.Length - 1)))
    End Function
    End Module
    
    0 讨论(0)
提交回复
热议问题