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
|