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
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 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
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
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
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
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.