(Print this page)

The FileSystemWatcher component
Published date: Friday, April 1, 2005
On: Moer and Éric Moreau's web site

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.


(Print this page)