Sunday, October 14, 2007

Tip in using FileSystemWatcher class, make use of InternalBufferSize

There are a few gotchas in implementing FileSystemWatcher. One of them that I have recently encountered is the use of InternalBufferSize property. Previously, I built a windows service that watches for new files created in a directory. The files are then processed and the data uploaded to a sql server database. During my testing and initial deploy, I didn't see any issue nor did I think of any potential problems. Until one day I noticed that it is not picking up all the files. Some files are being left out at random. After some investigation, I found out that this is happening because of an overflow in the buffer that supports the FileSystemWatcher. The default setting for this property is only at 8K, imagine it has to keep track of all the events and info about the files. Approximately according to this, 8K can only hold info for as much as 80 files at one time. So for me, my solution is to increase the buffer size to 32K, which is 32768. The setting should be in multiples of 4K as noted in msdn.

Here's how I implemented the class:

public partial class NXPFileWatcher : ServiceBase

    {

        FileSystemWatcher FSWatcher = null;

 

        public NXPFileWatcher()

        {

            InitializeComponent();

        }

 

        protected override void OnStart(string[] args)

        {

            try

            {

                FSWatcher = new FileSystemWatcher();

                FSWatcher.Path = ConfigurationManager.AppSettings["WatchPath"];

                FSWatcher.InternalBufferSize = 32768;

                FSWatcher.NotifyFilter = NotifyFilters.CreationTime;

                FSWatcher.Filter = "*.xml";

                FSWatcher.Created += new FileSystemEventHandler(FSWatcher_Created);

                FSWatcher.EnableRaisingEvents = true;

                SendNotification("has Started");

            }

            catch (Exception ex)

            {

                Log.Write(ex.Message);

            }

        }

 

        protected override void OnStop()

        {

            try

            {

                FSWatcher.Dispose();

                SendNotification("has Stopped");

            }

            catch (Exception ex)

            {

                Log.Write(ex.Message);

            }

        }

 

        protected override void OnPause()

        {

            OnStop();

        }

 

        protected override void OnContinue()

        {

            OnStart(null);

        }

 

        protected override void OnShutdown()

        {

            try

            {

                FSWatcher.Dispose();

                SendNotification("has stopped due to system shutting down");

            }

            catch (Exception ex)

            {

                Log.Write(ex.Message);

            }

        }

 

        private void FSWatcher_Created(object sender, FileSystemEventArgs e)

        {

            // Now open the file

            XmlFile xml = new XmlFile();

            xml.FullPath = e.FullPath;

            xml.Process();

        }

    }

3 comments:

Anonymous said...

Thank you very much !!!!

Two days to try to resolve this problem,

I add your site in my favorite !

Anonymous said...

Unfortunately the FileSystemWatcher does not behave as required when a huge file is being copied in the folder that is being watched. There is no way to get notified that the copy is complete and then run your logic.

I am doing a similar import to SQL DB using BCP. However my program fails when huge files are getting copied.

Any idea how this can be resolved?

You can email me on jdconsults@yahoo.co.in. Thanks

Anonymous said...

I ran into the same problem when large files are copied to the directory I'm watching. I put a try/catch inside a for loop. If the logic I used to read the file failed in the try statement, a thread.sleep(10000) would get executed in the catch section. I repeatd the for loop for up to 5 minutes (which would be 30 iterations of the for loop with 10 second waits in each iteration). If the try logic suceeded, I broke out of the for loop and continued onto the next step in the process.