Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

File Sysytem Watcher - multiple events being raised

Status
Not open for further replies.

bmpsu

Programmer
Jan 13, 2005
128
0
0
US
I originally posted in the asp.net forum and realized that this should probably be in this forum. Sorry for reposting here.

Original post - thread855-1450101

I have been away from this code for a few days. I am back working on this. I have tried a few different variations of the code below. I just can't seem to come up with a solution to only raise the change and create events once on a single file. In my searching on the web, this seems to be a common issue. I have not seen a true solution to this. Has anyone come across this.

Is there a way to check and see if a file has finished copying to a folder? It seems that when a file is copied to my watch directory, multiple events are raised during this process which triggers multiple attempts to copy my file.

Code:
Public Sub StartWatcher()

    Dim fwService As FileSystemWatcher
    Dim gblpvFilters As String() = {"*.zip", "*.txt", "*.dat", "*.csv"}

        For Each filter As String In gblpvFilters

           fwService = New FileSystemWatcher()
           fwService.Path = "C:\myFileWatcherTest\"
           fwService.IncludeSubdirectories = True
           'fwService.NotifyFilter = (NotifyFilters.LastWrite)
           fwService.Filter = filter

           AddHandler fwService.Changed, AddressOf MoveFile
           AddHandler fwService.Created, AddressOf MoveFile

           fwService.EnableRaisingEvents = True

        Next
End Sub

Private Sub MoveFile(ByVal source As Object, ByVal e As System.IO.FileSystemEventArgs)
        File.Copy(e.FullPath, "C:\move\" & e.Name, True)
End Sub
[\CODE]
 
I also should have mentioned that this code is broken down for posting purposes. I do have this code in a windows service application. I thought this code would display what I have and trying to do. If anyone wants the all of my code, I can post that. (But this time in the code blocks.)
 
So I find out that the file system watcher component is responding as it should. There are multiple events fired when a transaction like this occurs. The trick is to find a way to ignore/avoid the multiple events. This is what I came up with below.

I do have a problem I hope someone can help me out with. For no reasons I can see or catch in this code, my service stops watching. The service remains running and throws no exceptions or errors of any kind; but periodically with no rhyme or reason - stops. To make it work again, simply restart the service, then it works fine. I need this to run always.

The onTimedEvent() Sub always returns False for both conditions. I am not sure if this is why it stops watching my folders or not. That is the only thing I have been able to come up with. Do I have some threading issues?

Please Help!
Code:
Public Class FileWatcherService

    'Global page variables
    Private gblpvPathToWatch As StringCollection = My.Settings.PathToWatch
    Private gblpvFilters As StringCollection = My.Settings.Filters
    Private gblpvMoveTo As StringCollection = My.Settings.MoveTo
    Private gblpvCopyTimeSpan As Integer = My.Settings.CopyTimeSpan
    Private gblpvEventsTimer As Integer = My.Settings.EventsTimer

    Private gblpvFSWThread As Thread
    Private ht As Hashtable
    Private aTimer As New System.Timers.Timer()

    Public Sub New()
        MyBase.New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()
        ht = New Hashtable

        'add a timer to check status
        aTimer.Interval = gblpvEventsTimer
        aTimer.Enabled = True
        AddHandler aTimer.Elapsed, AddressOf OnTimedEvent
    End Sub

    Protected Overrides Sub OnStart(ByVal args() As String)
        Watch()
    End Sub

    Private Sub Watch()
        gblpvFSWThread = New Thread(AddressOf StartMonitor)
        gblpvFSWThread.Priority = ThreadPriority.Lowest
        gblpvFSWThread.Start()
    End Sub

    'Build FileSystemWatcher
    Private Sub StartWatch()
        Try
            For Each path As String In gblpvPathToWatch
                Me.fswComponent = New FileSystemWatcher()
                Me.fswComponent.Path = path.Trim
                Me.fswComponent.NotifyFilter = (NotifyFilters.CreationTime Or _
                                                NotifyFilters.FileName Or _
                                                NotifyFilters.LastAccess Or _
                                                NotifyFilters.LastWrite Or _
                                                NotifyFilters.Size)
                Me.fswComponent.IncludeSubdirectories = True
                Me.fswComponent.Filter = "*.*"

                AddHandler Me.fswComponent.Changed, AddressOf MoveFile
                AddHandler Me.fswComponent.Created, AddressOf MoveFile
                AddHandler Me.fswComponent.Error, AddressOf WatcherError

                Me.fswComponent.EnableRaisingEvents = True
            Next
        Catch ex As Exception
            Watch()
            Thread.CurrentThread.Join()
        End Try
    End Sub

    Private Sub WatcherError(ByVal source As Object, ByVal e As System.IO.ErrorEventArgs)
        'We need to create new version of the object because the old one is now corrupted
        Dim watchException As Exception = e.GetException()
        Me.fswComponent.EnableRaisingEvents = False
        Me.fswComponent.Dispose()
        Watch()
        Thread.CurrentThread.Join()
    End Sub

    Private Sub MoveFile(ByVal source As Object, ByVal e As System.IO.FileSystemEventArgs)
        Dim aSourceSplit As String() = Nothing
        Dim strSourceFolder As String = Nothing
        Dim strDestDirectory As String = Nothing
        Dim strFileMoveTo As String = Nothing
        Dim ChangeTypeAccepted As Boolean = False

        'start monitoring for the next file (was missing files before?)
        Watch()

        If IsExtValid(e.FullPath) Then 'checks to see if file is correct extension
            If IsFileLocked(e.FullPath) = False Then 'tries to open file

                'loop through each gblMoveTo folder and copy the file to this folder
                For Each moveToPath As String In gblpvMoveTo
                    strFileMoveTo = moveToPath & e.Name

                    If HasBeenMoved(strFileMoveTo) = False Then
                        ht.Add(strFileMoveTo, DateTime.Now.ToString)
                        File.Copy(e.FullPath, strFileMoveTo, True)
                    End If
                Next

            End If
        End If

        'this ends the activites of this thread and leaves the new watcher running.
        Thread.CurrentThread.Join()
    End Sub

    'check to see if file has been moved within the gblpvCopyTimeSpan value
    Private Function HasBeenMoved(ByVal filePath As String) As Boolean
        Dim result As Boolean = False
        Dim mSeconds As Double
        Dim dtNow As DateTime = DateTime.Now
        Dim fileNameDate As DateTime = Nothing

        Try
            If ht.ContainsKey(filePath) Then
                fileNameDate = CType(ht.Item(filePath), DateTime)
                mSeconds = dtNow.Subtract(fileNameDate).TotalMilliseconds

                If mSeconds > gblpvCopyTimeSpan Then
                    ht.Remove(filePath)
                Else
                    result = True
                End If
            End If
        Catch ex As Exception
            result = False
            If Not ht Is Nothing Then
                ht.Clear()
            End If
        End Try

        Return result
    End Function

    'Checks to see if file exists and if file has a valid extention from config
    Private Function IsExtValid(ByVal filePath As String) As Boolean
        Dim result As Boolean = False

        If File.Exists(filePath) Then
            For Each filter As String In gblpvFilters
                If Path.GetExtension(filePath).ToLower = filter.Trim.ToLower Then
                    result = True
                    Exit For
                End If
            Next
        End If

        Return result
    End Function

    'Trys to open file. If fails then file is still being written to ftp directory and returns false.
    Private Function IsFileLocked(ByVal filePath As String) As Boolean
        Dim fs As FileStream = Nothing
        Dim result As Boolean = False

        Try
            fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None)
        Catch ex As Exception
            result = True
        Finally
            If Not fs Is Nothing Then
                fs.Flush()
                fs.Close()
                fs.Dispose()
            End If
        End Try

        Return result
    End Function

    Private Sub OnTimedEvent(ByVal source As Object, ByVal e As ElapsedEventArgs)
        If (gblpvFSWThread.IsAlive = False) AndAlso hasRestarted = False Then
            'Check to see if the thread is alive
            Me.fswComponent.EnableRaisingEvents = False
            Me.fswComponent.Dispose()
            Watch()
        ElseIf ((gblpvFSWThread.ThreadState And ThreadState.Unstarted) <> ThreadState.Running) AndAlso hasRestarted = False Then
            'Check to see if thread is running
            Me.fswComponent.EnableRaisingEvents = False
            Me.fswComponent.Dispose()
            Watch()
        End If
    End Sub
 
Looks like I might be experiencing a term called "race condition - when two or more threads perform an operation, and the result of the operation depends on unpredictable timing factors; when each thread completes the operation".

This is all very new to me, so I am going to sleep on it. Anyone familiar with this and is this my problem? Or am I way off base? This seems to logically explain why it stops watching and giving me no thrown errors or exceptions.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top