Click here to Skip to main content
13,628,146 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

6.3K views
16 bookmarked
Posted 6 Dec 2017
Licenced CPOL

FileSystemWatcher vs Locked Files

, 7 Dec 2017
Rate this:
Please Sign up or sign in to vote.
You monitor a folder for new files with FileSystemWatcher and try to move those them on arrival. You get an IOException: ...being used by another process. The copying had not finished. Here, I offer a fairly robust solution to this.

Introduction

You’ve just written your nice shiny new application to monitor a folder for new files arriving and added the code to send that file off somewhere else and delete it. Perhaps you even spent some time packaging it in a nice Windows Service. It probably behaved well during debugging. You move it into a test environment and let the manual testers loose. They copy a file, your eager file watcher spots the new file as soon as it starts writing and does the funky stuff and BANG!… an IOException:

The process cannot access the file X because it is being used by another process.

The copying had not finished before the event fired. It doesn’t even have to be a large file as your super awesome watcher is just so efficient.

A Solution

  1. When a file system event occurs, store its details in Memory Cache for X amount of time
  2. Setup a callback, which will execute when the event expires from Memory Cache
  3. In the callback, check the file is available for write operations
  4. If it is, then get on with the intended file operation
  5. Else, put it back in Memory Cache for X time and repeat the above steps

It would make sense to track and limit the number of retry attempts to get a lock on the file.

Code Snippets

I’ve built this on top of the code discussed in a previous post on dealing with multiple FileSystemWatcher events.

The complete code for this example solution can be found here.

When a file system event is handled, store the file details and the retry count, using a simple POCO, in MemoryCache with a timer of, something like 60 seconds:

private void OnCreated(object source, FileSystemEventArgs e)
{
    _cacheItemPolicy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(CacheTimeSeconds);

    var fileData = new CacheItemValue()
    {
         FilePath = e.FullPath,
         RetryCount = 0,
         FileName = e.Name
    };
    _memCache.AddOrGetExisting(e.Name, fileData, _cacheItemPolicy);
}

A simple POCO:

class CacheItemValue
{
    public string FileName { get; set; }
    public string FilePath { get; set; }
    public int RetryCount { get; set; }
}

In the constructor, I initialised my cache item policy with a callback to execute when these cached POCOs expire:

_cacheItemPolicy = new CacheItemPolicy
{
    RemovedCallback = OnRemovedFromCache
};

The callback itself…

  1. Increments the number retries
  2. Tries and gets a lock on the file
  3. If still locked, put it back into the cache for another 60 seconds (repeat this MaxRetries times)
  4. Else, get on with the intended file operation
private void OnRemovedFromCache(CacheEntryRemovedArguments args)
{
    // Checking if expired, for a bit of future-proofing
    if (args.RemovedReason != CacheEntryRemovedReason.Expired) return;

    var cacheItemValue = (CacheItemValue)args.CacheItem.Value;
    if (cacheItemValue.RetryCount > MaxRetries) return;

    if (IsFileLocked(cacheItemValue.FilePath))
    {
        cacheItemValue.RetryCount++;
        _cacheItemPolicy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(CacheTimeSeconds);
              
        _memCache.Add(cacheItemValue.FileName, cacheItemValue, _cacheItemPolicy); 
     }

     // Now safe to perform the file operation here...
}

Other Ideas / To Do….

  • Could also store the actual event object
  • Could explore options for non-volatile persistence
  • Might find sliding expiration more appropriate in some scenario

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

BenHall_io
Software Developer (Senior)
United Kingdom United Kingdom
Left an enjoyable career teaching Computer Science & programming in 2016 and started out in software development.

Have an unhealthy obsession with .NET / C# internals.

Currently a Senior Software Engineer at the United Kingdom Hydrographic Office.

C# / ASP.NET
NServiceBus
SQL Server
JavaScript, jQuery etc…
PowerShell + DSC

You may also be interested in...

Pro

Comments and Discussions

 
QuestionWhy not just use Threads instead? Pin
Alex Schunk3-Jan-18 12:08
memberAlex Schunk3-Jan-18 12:08 
QuestionGood read Pin
Dankwansere10-Dec-17 16:10
memberDankwansere10-Dec-17 16:10 
GeneralMessage Closed Pin
6-Dec-17 20:12
memberMember 135627586-Dec-17 20:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.180712.1 | Last Updated 7 Dec 2017
Article Copyright 2017 by BenHall_io
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid