VB.Net - FileSystemWatch Version 2

Version 1 of the VB.Net - FileSystemWatcher was pretty good. It had one basic flaw, If you are watching a location that disappears (for example a folder on a server that goes down), then the FileSystemWatcher will stop working. When the location reappears, the FileSystemWatcher does not recover.

To workaround this, you need a second object - like a timer, which checks to see if what the FileSystemWatcher is still available. If it is then great. If it isn’t then take the FileSystemWatcher offline until the location is restored.

I found a nice example George Oakes - Advanced FileSystemWatcher - Code Project - 21 Sep 2006

It starts off by extending the FileSystemWatcher class.

Here is what my code looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Public Class AdvancedFileSystemWatcher
Inherits FileSystemWatcher 'Inherit the base class

' version 2 credits
' http://www.codeproject.com/Articles/15656/Advanced-FileSystemWatcher

Public Event NetworkPathUnavailable(ByVal Message As String)
Public Event NetworkPathAvailable(ByVal Message As String)
Private WithEvents _myTimer As New System.Timers.Timer
Private _isNetworkUnavailable As Boolean = False
Private _thePath As String = ""

Public Sub New()
'use the timers default interval of 100 milliseconds and start the timer
_myTimer.Start()
End Sub


Public Sub New(ByVal iNetworkWatchInterval As Int32, thePath As String)
'check to see if the interval is a positive integer value and set it.
If iNetworkWatchInterval > 0 Then
_myTimer.Interval = iNetworkWatchInterval
Else 'The user entered a
_myTimer.Interval = 1000
End If
_myTimer.Start()
_thePath = thePath
End Sub

Public Sub close()
_myTimer.Stop()
_myTimer.Dispose()
End Sub

Private Sub _myTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles _myTimer.Elapsed
Dim myFolder As DirectoryInfo

Try
myFolder = New DirectoryInfo(_thePath)

If Not _isNetworkUnavailable Then
If Not myFolder.Exists Then
_isNetworkUnavailable = True
RaiseEvent NetworkPathUnavailable("A network error has occured or the path does not exist anymore")
End If
Else
If myFolder.Exists Then
_isNetworkUnavailable = False
RaiseEvent NetworkPathAvailable("The network or path has been restored")
End If
End If
Catch ex As Exception
Throw
Finally
myFolder = Nothing
End Try

End Sub

End Class

As you can see, this class is a wrapper around the FileSystemWatcher, which checks the watched location and throws errors when that location is no longer available. The code demonstrated in version 1 would be supplemented to support some new events NetworkPathUnavailable and NetworkPathAvailable. Here’s what the resulting program looks like

Here is what a Stripped Down code looks like now

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
Imports System.IO
Imports System.Configuration
Imports System.Windows.Forms
Imports System.ComponentModel

Public Class theMainV2
Inherits System.Windows.Forms.Form
'
' version 2 came from
' http://www.codeproject.com/Articles/15656/Advanced-FileSystemWatcher
' I moved portions into this project
'
Private Delegate Sub updatetextbox(ByVal newLabel As String)
Private WithEvents anAdvFileSystemWatcher As AdvancedFileSystemWatcher
Private Watch4Changes As Boolean = False

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Dim cmdline As String _
= Command()
Dim cmd() As String _
= cmdline.Split(" ")

Console.WriteLine("Directory Watch Utility")
Console.WriteLine("Creates a log of files added, modified, created, deleted, or renamed for a specific directory")
Console.WriteLine("")
Console.WriteLine("Usage")
Console.WriteLine(" DirWatch DirectoryToWatch LogFileName")
Console.WriteLine("")
If cmd.Length < 2 Then
End
End If
txtFolderToWatch.Text = cmd(0)
txtCSVFileName.Text = cmd(1)

Console.WriteLine("Path=" & txtFolderToWatch.Text)
Console.WriteLine("Logging file=" & txtCSVFileName.Text)

StartFileSystemWatcher()

End Sub


Private Sub StartFileSystemWatcher()
Try
'Create a new Advanced File System Watcher and set the network scan interval
anAdvFileSystemWatcher = New AdvancedFileSystemWatcher(5000, txtFolderToWatch.Text)

'Add the handlers to handle the filesystemwatcher events
AddHandler anAdvFileSystemWatcher.Created, AddressOf OnCreated
AddHandler anAdvFileSystemWatcher.Changed, AddressOf OnChanged
AddHandler anAdvFileSystemWatcher.Deleted, AddressOf Ondeleted
AddHandler anAdvFileSystemWatcher.Renamed, AddressOf OnRenamed

anAdvFileSystemWatcher.Path = txtFolderToWatch.Text
anAdvFileSystemWatcher.IncludeSubdirectories = True
anAdvFileSystemWatcher.EnableRaisingEvents = True

Log(",,Dir Watcher Program now watching - " & txtFolderToWatch.Text)
Catch ex As Exception
Log(",,Error " & ex.GetBaseException.ToString)
End Try
End Sub

Private Sub StopFileSystemWatcher()
Try
anAdvFileSystemWatcher.EnableRaisingEvents = False
Log(",,Dir Watcher Program no longer watching - " & txtFolderToWatch.Text)
Catch ex As Exception
Throw ex
End Try
End Sub

Public Sub OnChanged(ByVal source As Object, ByVal e As FileSystemEventArgs)
If Watch4Changes Then
Dim fullpath As String = e.FullPath
Dim filename As String = Path.GetFileName(fullpath)
Dim thepath As String = Path.GetDirectoryName(fullpath)

Log(thepath & ", " & filename & ", changed - " & e.ChangeType)
End If
End Sub

Public Sub OnCreated(ByVal source As Object, ByVal e As FileSystemEventArgs)
Try
Dim fullpath As String = e.FullPath
Dim filename As String = Path.GetFileName(fullpath)
Dim thepath As String = Path.GetDirectoryName(fullpath)

Log(thepath & ", " & filename & ", created")
Catch ex As Exception
Throw ex
End Try

End Sub

Public Sub Ondeleted(ByVal source As Object, ByVal e As FileSystemEventArgs)
Try
Dim fullpath As String = e.FullPath
Dim filename As String = Path.GetFileName(fullpath)
Dim thepath As String = Path.GetDirectoryName(fullpath)

Log(thepath & ", " & filename & ", deleted")
Catch ex As Exception
Throw ex
End Try

End Sub

Public Sub OnRenamed(ByVal source As Object, ByVal e As RenamedEventArgs)
Try
Dim fullpath As String = e.OldName
Dim filename As String = Path.GetFileName(fullpath)
Dim thepath As String = Path.GetDirectoryName(fullpath)

Dim newfilename As String = Path.GetFileName(e.Name)
Log(thepath & ", " & filename & ", renamed to " & newfilename)

Catch ex As Exception
Throw ex
End Try

End Sub

Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Me.Close()
Me.Dispose()

End Sub

Private Sub anAdvFileSystemWatcher_NetworkPathAvailable(ByVal Message As String) Handles anAdvFileSystemWatcher.NetworkPathAvailable
Try
'Ok the network path is available now
StartFileSystemWatcher()
Catch ex As Exception
Log(",,Error in NetworkPathAvailable - " & ex.GetBaseException.ToString)
End Try
End Sub

Private Sub anAdvFileSystemWatcher_NetworkPathUnavailable(ByVal Message As String) Handles anAdvFileSystemWatcher.NetworkPathUnavailable
Try
'There was a network error we need to destroy the filesystemwatcher object and try to recreate it.
'Send an email saying there is a netowrk error????
'SendMail

'now we have to stop and kill the current filesystemwatcher. we will recreate it in the available event.
StopFileSystemWatcher()
anAdvFileSystemWatcher.Dispose()

Catch ex As Exception
Throw ex
End Try
End Sub

Public Sub Log(message As String)
If lblComputerName.Text.Length > 0 Then
message += ", " + lblComputerName.Text
End If
If lblUserName.Text.Length > 0 Then
message += ", " + lblUserName.Text
End If
Try
Using sw As StreamWriter = File.AppendText(txtCSVFileName.Text)
sw.WriteLine(DateTime.Now.ToString & ", " + message)
End Using

Catch ex As Exception
Console.WriteLine(DateTime.Now.ToString & ",, *** ERROR TRYING TO WRITE TO LOG - " & ex.Message)
End Try
Console.WriteLine(DateTime.Now.ToString & ", " + message)
End Sub

Private Sub theMainV2_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
Log(",,Dir Watcher Program Ending - " & txtFolderToWatch.Text)
End Sub
End Class

Another sample

The FileSystemWatcher class is used to create a function that watches activities being performed
on files sitting in a folder. When running it will throw an event whenever a file is added, changed,
deleted, or renamed.

Actually I found several suprisingly good websites.

Note: I created a form, and placed the code within the form. I did this because If I created a main() routine,
it would execute, and then terminate and the FileSystemWatcher would not be running. By creating the form
I know that the FileSystemWatcher is functioning because the window is open.

On the form I added a textbox control:

  • A directory to watch named ‘txtDir’. it is populated with the folder being watched

Here is a stripped down version of the code behind the form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Imports System
Imports System.IO
Imports System.Diagnostics
Imports System.Windows.Forms

Public Class theMain
Public watchfolder As FileSystemWatcher

Private Sub theMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim cmdline As String _
= Command()
Dim cmd() As String _
= cmdline.Split(" ")

Console.WriteLine("Directory Watch Utility")
Console.WriteLine("Creates a log of files added, modified, created, " _
& "deleted, or renamed for a specific directory")
Console.WriteLine("")
Console.WriteLine("Usage")
Console.WriteLine(" DirWatch DirectoryToWatch ")
Console.WriteLine("")
If cmd.Length < 2 Then
End
End If
txtDir.Text = cmd(0)

watchfolder = New System.IO.FileSystemWatcher()
watchfolder.Path = txtDir.Text
watchfolder.NotifyFilter = IO.NotifyFilters.DirectoryName
watchfolder.NotifyFilter = watchfolder.NotifyFilter Or
IO.NotifyFilters.FileName
watchfolder.NotifyFilter = watchfolder.NotifyFilter Or
IO.NotifyFilters.Attributes
'AddHandler watchfolder.Changed, AddressOf logchange ' TMI
AddHandler watchfolder.Created, AddressOf logchange
AddHandler watchfolder.Deleted, AddressOf logchange
AddHandler watchfolder.Renamed, AddressOf logrename
watchfolder.EnableRaisingEvents = True

Log("Dir Watcher Program Started - " & txtDir.Text)
End Sub


Private Sub logchange(ByVal source As Object _
, ByVal e As System.IO.FileSystemEventArgs)

If e.ChangeType = IO.WatcherChangeTypes.Changed Then
Log(e.FullPath & ", modified")
End If
If e.ChangeType = IO.WatcherChangeTypes.Created Then
Log(e.FullPath & ", created")
End If
If e.ChangeType = IO.WatcherChangeTypes.Deleted Then
Log(e.FullPath & ", deleted")
End If
End Sub
Public Sub logrename(ByVal source As Object _
, ByVal e As System.IO.RenamedEventArgs)
Log(e.OldName & ", renamed to " & e.Name)
End Sub

Public Sub Log(message As String)
Console.WriteLine(DateTime.Now.ToString & ", " + message)
End Sub

Private Sub theMain_FormClosing(sender As Object _
, e As FormClosingEventArgs) Handles Me.FormClosing
Log("Dir Watcher Program Ending - " & txtDir.Text)
End Sub
End Class

My full version sends the output to a log file - but that small bit of extra code was not
needed for this example.

  • [[FileSystemWatchAdvanced]] - here’s another article, more evolved.