This component listens to the file system notifications events of Windows. This can component can be used in your application to monitor a folder and to receive events when there is any activity in that folder. Any activity? Well those to which you subscribed. For example, you can filter to only some type of files (by giving proper extensions) or only some actions (only creation, or only deletion, …).
Be advised that this component will not work on Windows 98 or Windows ME (only on Windows NT 4.0 and later).
The basics
This component is part of the System.IO namespace. Since it is a component, you can find it right in your toolbox under the Components tab and you can easily add one to your form.
Figure 1: The FileSystemWatcher component in the toolbox
Go ahead, create a new Windows application. Add a FileSystemWatcher component. Don’t change any of its properties since we will do it using code later. Also add a checkbox control (mine is named chkMonitoring) that we will use has a toggle to start/stop the monitoring. Finally, add a listbox control (mine is named lstEvents) to display some status when our application will be running.
Setting the main properties
Our first task (after the interface has been created) is to set the FileSystemWatcher component’s properties. I like to create a private method whose job is exclusively to set the component’s properties and to set event handlers.
Start by looking at this code of this method:
Private Sub InitFSW()
With Me.FileSystemWatcher1
'Set the path we want to monitor
.Path = "C:\temp\UTMAG"
.IncludeSubdirectories = False
' Filter to only watch text files.
.Filter = "*.csv"
'Set events to which we want to react
.NotifyFilter = (NotifyFilters.LastWrite Or _
NotifyFilters.FileName Or _
NotifyFilters.DirectoryName)
' Add event handlers.
AddHandler .Changed, AddressOf FSWHandler1
AddHandler .Created, AddressOf FSWHandler1
AddHandler .Deleted, AddressOf FSWHandler1
AddHandler .Renamed, AddressOf FSWHandlerRename
AddHandler .Error, AddressOf FSWError
End With
End Sub
The first 2 properties to set are the Path property and the IncludeSubdirectories property. The path property contains the root directory you want to monitor. Only one root folder can be monitored using a single FileSystemWatcher component. It supports Universal Naming Convention (UNC) paths allowing you to provide network folder path without having to map drives first. If you want to monitor subdirectories using the same component, just set the IncludeSubdirectories property to true (the default is false).
The Filter property is used to determine what files are monitored. It can be a filename (ie MyFileName.ext) or an extension (ie *.txt). The default is *.*.
The NotifyFilter property lets you set the type of changes to watch for. This property allows you to combine values using a bitwise combination (you have to put the Or operator between each value you want). There are 8 values allowed: Attributes, CreationTime, DirectoryName, FileName, LastAccess, LastWrite, Security, and Size. The default combination is LastWrite, FileName, DirectoryName.
The last step of this method is to subscribe to events (the 5 AddHandler lines). This is done by using the AddHandler statement to direct events to appropriate methods. This component offers only 5 methods that are Changed, Created, Deleted, Error, Renamed (I don’t count Dispose as it is inherited from Component and not very useful here). I think you can guess when these events are triggered by reading their name. Three of these methods (Changed, Created, and Deleted) are using the same signatures. The two others events have their own signature. More on these signatures later. If you are not interested by some of these events, you don’t have to create delegate and you don’t have to write the AddHandler line for this event.
This method is called from the Load event of my form.
Processing events
As I just said, only five events are triggered by this component. Three of them have the same signature (it is the second parameters that varies depending on the event). Here is code I have:
Private Sub FSWHandler1( _
ByVal sender As Object, _
ByVal e As System.IO.FileSystemEventArgs)
'Signature to use for the Created, Deleted, and Changed events
lstEvents.Items.Add("FSWHandler1 detected some activity at " & _
Date.Now.ToString)
lstEvents.Items.Add(" Action Type = " & e.ChangeType.ToString)
lstEvents.Items.Add(" File = " & e.FullPath)
End Sub
Private Sub FSWHandlerRename( _
ByVal sender As Object, _
ByVal e As System.IO.RenamedEventArgs)
'Signature to use for the Renamed event
lstEvents.Items.Add("FSWRename detected some activity at " & _
Date.Now.ToString)
lstEvents.Items.Add(" File " & e.OldFullPath)
lstEvents.Items.Add(" was rename to " & e.FullPath)
End Sub
Private Sub FSWError( _
ByVal sender As Object, _
ByVal e As System.IO.ErrorEventArgs)
'Signature to use for the Error event
lstEvents.Items.Add("FSWError detected some activity at " & _
Date.Now.ToString)
lstEvents.Items.Add(" " & e.GetException.ToString)
End Sub
These events handlers are only reporting status into the listbox so that you can see what is going on.
Starting the subscription
When the application starts (or when the form is loaded) the monitoring will not be effective. We have set the properties but we haven’t told it to start! That’s where we will use our checkbox.
You have to set the EnableRaisingEvents property to True to start receiving events. Here is the code I have in the CheckedChanged event of the checkbox control.
Private Sub chkMonitoring_CheckedChanged( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles chkMonitoring.CheckedChanged
If chkMonitoring.Checked Then
' start monitoring.
Me.FileSystemWatcher1.EnableRaisingEvents = True
lstEvents.Items.Add("+++++ Monitoring started at " & _
Date.Now.ToString & " +++++")
Else
' stop monitoring.
Me.FileSystemWatcher1.EnableRaisingEvents = False
lstEvents.Items.Add("----- Monitoring stopped at " & _
Date.Now.ToString & "-----")
End If
End Sub
Anything you will do after the EnableRaisingEvents has been set to True that fits your settings will trigger an event into your application.
Take some time to play with your application and to change some settings before going on.
Some problems and limitations
I am using this component in an application that is monitoring the creation of files in a network folder. I found some problems and limitations. Here are the most two important.
The major limitations I found is that you can specify only one extension into the Filter property. What if you want to monitor .csv and .txt files at the same time? In my application, I found myself setting the Filter property to empty (ie “”) and checking the extension myself in the events and processing only required extensions. I would like to see it fixed in the next version. The same limitation applies to the Path property but wasn’t a limitation in my case since I only monitor a single folder (and its subdirectories).
After a week that my application was running correctly, it suddenly stopped without any errors. My application was still running but stopped receiving events when files where created. After digging, I discovered that the network connection has cut for about 2 minutes the night before. Since the cut, no events were raised. This behaviour can easily be tested simply by deleting the root folder you set in the Path property. I had to find a solution! Here is one I will share with you. You need to do two modifications to the code above. The first one concerns the Error event handler. Change the code you have for this event for this new one:
Private Sub FSWError( _
ByVal sender As Object, _
ByVal e As System.IO.ErrorEventArgs)
'Signature to use for the Error event
lstEvents.Items.Add("FSWError detected some activity at " & _
Date.Now.ToString)
lstEvents.Items.Add(" " & e.GetException.ToString)
FileSystemWatcher1.EnableRaisingEvents = False
Do Until FileSystemWatcher1.EnableRaisingEvents
Application.DoEvents()
Try
InitFSW()
FileSystemWatcher1.EnableRaisingEvents = True
lstEvents.Items.Add(" Now back to work!!! " & _
Date.Now.ToString)
Catch ex As Exception
FileSystemWatcher1.EnableRaisingEvents = False
lstEvents.Items.Add(" Still have problems with the folder at " & _
Date.Now.ToString)
Threading.Thread.Sleep(5000)
End Try
Loop
End Sub
When the folder is deleted (or when the network connection gets cut), an error is raised and the FileSystemWatcher object gets corrupted. This code simply tries to reset the FileSystemWatcher properties and to re-enable it. If this succeeds, it means that everything is back to normal.
I have said 2 modifications. This second modification is that you need to remove the handler to the events when you recreate the FileSystemWatcher object. You need to add these lines to the InitFSW method before setting the Path property:
'Remove handlers of the previous instance
RemoveHandler .Changed, AddressOf FSWHandler1
RemoveHandler .Created, AddressOf FSWHandler1
RemoveHandler .Deleted, AddressOf FSWHandler1
RemoveHandler .Renamed, AddressOf FSWHandlerRename
RemoveHandler .Error, AddressOf FSWError
If you don’t remove the handlers and your FileSystemWatcher has errors and is recreated, you will receive one event for every time the AddHandler has been executed.
Figure 2: The demo app in action
Conclusion
This control is very useful when you need to take action when activities are occurring into designated folders. It is a very easy to use component with some limitations but very good overall.
I hope you appreciated the topic and see you next month.