Tim Hibbard

CEO for EnGraph software
posts - 626, comments - 1586, trackbacks - 459

My Links

News



Add to Google

Twitter












Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

EnGraph Blogs

Links

Other

Roll

Extending the IO.FileSystemWatcher class

The FileSystemWatcher is very useful when you need to be notified of changes made to a specific folder. You can raise an event when a new file is created, or deleted and you can also filter by file type and choose if you wish to monitor sub directories. The only problem with the object comes with large file transfers. The created event is raised as soon as the file transfer begins to your watched folder, not when the file has finished transferring. So if you need to move a file or pull data out a file, you will get an error if the file hasn't completely copied yet. In one of our enterprise GPS solutions, we transfer GPS data over VPN and we need to know when the file has fully transferred.

I extended the FileSystemWatcher class and added a new event, ChangeComplete. This event is raised after a new file is completely transferred or is renamed. I accomplished this by adding files from the created event to a queue and then trying to open them exclusively every two seconds. If the open call failed, then the file is not finished transferring. If the call succeeds, I close the file and then I raise the ChangeComplete event. I've posted the code below:

Public Class ExtendedFileSystemWatcher
#Region " Inherits "
    Inherits IO.FileSystemWatcher
#End Region
#Region " Variables "
    Dim InQueue As New ArrayList
    Dim Watcherthread As Threading.Thread
    Dim RunWatcher As Boolean = False
#End Region
#Region " Events "
    Public Event ChangeComplete(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs)
#End Region
#Region " Properties "

    Public Overloads Property EnableRaisingEvents() As Boolean
        Get
            Return MyBase.EnableRaisingEvents
        End Get
        Set(ByVal value As Boolean)
            If value = True Then
                RunWatcher = True
                Watcherthread = New Threading.Thread(AddressOf WatchForCompletedFiles)
                Watcherthread.IsBackground = True
                Watcherthread.Start()
            Else
                RunWatcher = False
                Watcherthread.Abort()
            End If
            MyBase.EnableRaisingEvents = value
        End Set
    End Property


    Public ReadOnly Property FileInQueue() As System.IO.FileSystemEventArgs()
        Get
            Try
                Dim rv(InQueue.Count - 1) As System.IO.FileSystemEventArgs
                InQueue.CopyTo(rv)
                Return rv
            Catch ex As Exception
                Return Nothing
            End Try
        End Get
    End Property
#End Region
#Region " My.Base Events "
    Private Sub ExtendedFileSystemWatcher_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles Me.Created
        Try
            InQueue.Add(e)
        Catch ex As Exception
            MyBase.OnError(New IO.ErrorEventArgs(ex))
        End Try
    End Sub
    Private Sub ExtendedFileSystemWatcher_Renamed(ByVal sender As Object, ByVal e As System.IO.RenamedEventArgs) Handles Me.Renamed
        Try
            Dim x As New System.IO.FileSystemEventArgs(IO.WatcherChangeTypes.Created, New IO.FileInfo(e.FullPath).DirectoryName, e.Name)
            InQueue.Add(e)
        Catch ex As Exception
            MyBase.OnError(New IO.ErrorEventArgs(ex))
        End Try
    End Sub

#End Region
#Region " Methods "
    Private Sub WatchForCompletedFiles()
        Do While RunWatcher = True
            If InQueue.Count > 0 Then
                Dim e As System.IO.FileSystemEventArgs
                Dim intX As Integer
                For intX = 0 To InQueue.Count - 1
                    Try
                        e = InQueue.Item(intX)
                        Dim fs As New IO.FileStream(e.FullPath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.None)
                        fs.Close()
                        Application.DoEvents()
                        InQueue.RemoveAt(intX)
                        RaiseEvent ChangeComplete(Me, e)
                    Catch ex As Exception
                        'if it doesn't work, we don't really care
                        'just move on to the next iteration
                    End Try
                Next
            End If
            Threading.Thread.Sleep(2000)
            Application.DoEvents()
        Loop
    End Sub
#End Region
#Region " Constructors "
    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(ByVal Path As String)
        MyBase.New(Path)
    End Sub
    Public Sub New(ByVal Path As String, ByVal Filter As String)
        MyBase.New(Path, Filter)
    End Sub
#End Region
End Class

Print | posted on Friday, September 08, 2006 3:10 PM |

Feedback

Gravatar

# re: Extending the IO.FileSystemWatcher class

Great job! Your coding was very easy to understand. I added one thing though, to coutneract a problem I was having of having two created events called for the same file. (Caused by the way Adobe Illustrator and Adobe Acrobat work together to make a file). This is what I added to the ExtendedFileSystemWatcher_Created event:

For Each eTest As System.IO.FileSystemEventArgs In InQueue
If eTest.FullPath = e.FullPath Then Exit Sub
Next
11/15/2006 11:51 AM | Doug Neiner
Gravatar

# re: Extending the IO.FileSystemWatcher class

This is a really useful bit of code, especially with Doug Neiner's addition. I have been suffering all sorts of problems with a combination of large files and Adobe's quirks!
1/8/2009 3:38 AM | Mike Wedge
Gravatar

# re: Extending the IO.FileSystemWatcher class

CAn I use Application.DoEvents in Windows Service ?

I use FileSystemWatcher in Windows Service and I have those problems.

Thanks.
8/1/2009 10:04 AM | espinete
Gravatar

# re: Extending the IO.FileSystemWatcher class

I believe there is a typo in the renamed event handler. In that method a local FileSystemEventArgs object is created, but then not used. Shouldn't it be added to the queue rather than the RenamedEvent args passed into the method?

Dim x As New System.IO.FileSystemEventArgs(IO.WatcherChangeTypes.Created, New IO.FileInfo(e.FullPath).DirectoryName, e.Name)
InQueue.Add(e) ' shouldn't this be x ?
9/1/2010 1:49 PM | Dave
Gravatar

# re: Extending the IO.FileSystemWatcher class

Hi! this is awesome. I translate ir to C# but I think there is a litthe problem. As your'e working with threads, when you remove the file that doesn't throw an error with the FileStream, you do it with the index. Isn't better to remove it, searching the item e? It can de done with just .Remove(e), or after 3.5 a lambda. Of course, changing the arraylist to List<FileSystemEventArgs>.
Regards!
8/6/2011 3:06 PM | BlackCath
Gravatar

# re: Extending the IO.FileSystemWatcher class

Can you give me an example of code on how this class extension works?
2/23/2012 8:02 PM | Jack C Shofner
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification:
 
 

Powered by: