mitch

Il blog di Mirko Gatti
posts - 7, comments - 80, trackbacks - 8

Friday, September 22, 2006

Problemi con l'evento Created di FileSystemWatcher

Di recente ho dovuto utilizzare System.IO.FileSystemWatcher per osservare una cartella ed accorgermi della creazione di nuovi files al suo interno.
In quell'occasione ho fatto esperienza di comportamenti anomali legati agli eventi di notifica dei cambiamenti nei files o cartelle.
Il caso fastidioso si verifica quando un file di grosse dimensioni arriva nella cartella: l'evento Created viene sparato quando inizia la scrittura fisica su disco, e non alla fine. Se all'interno del metodo legato all'evento si cerca di aprire il file si ottiene una bella eccezione di tipo System.IOException.

using System;
using System.IO;

namespace FSWTest
{
    
class Program
    {
        
static void Main(string[] args)
        {
            FileSystemWatcher fsw =
                
new FileSystemWatcher(@"C:\Temp\FSWTest");

            fsw.Created += 
new FileSystemEventHandler(fsw_Created);
            fsw.EnableRaisingEvents = 
true;

            Console.Read();
        }

        
static void fsw_Created(object sender, FileSystemEventArgs e)
        {
            Console.WriteLine("{0} {1}", e.ChangeType, e.Name);
        }
    }
}

I miei colleghi ed io ipotizzavamo che questo comportamento fosse dovuto ad un baco del FileSystemWatcher del Framework 1.1, ma nel 2.0 le cose non cambiano.

Durante la ricerca di informazioni all'interno di gruppi di discussione ho avuto l'impressione che il baco sia localizzato a livello delle API di Windows:
- le API FileSystemWatcher di Windows non sparano l'evento Created nel modo giusto
- il FileSystemWatcher palleggia semplicemente gli eventi
- l'applicativo .NET deve fare attenzione a non aprire subito i nuovi files notificati

Una possibilità, anche se poco elegante, di gestire il comportamento anomalo dell'evento Created di FileSystemWatcher è quella di provare ad aprire il file fino a quando non si ottengono più eccezioni.

    //Questa è la mia classe wrapper di System.IO.FileSystemWatcher
    
internal class FileSystemWatcherWrapper
    {
        FileSystemWatcher fileSystemWatcher = 
null;

        
public FileSystemWatcherWrapper()
        {
            
this.fileSystemWatcher = new FileSystemWatcher();
            
this.fileSystemWatcher.Created +=
                
new FileSystemEventHandler(fileSystemWatcher_Created);
        }

        
public string Path
        {
            
get return this.fileSystemWatcher.Path; }
            
set this.fileSystemWatcher.Path = value; }
        }

        
public string Filter
        {
            
get return this.fileSystemWatcher.Filter; }
            
set this.fileSystemWatcher.Filter = value; }
        }

        
public bool EnableRaisingEvents
        {
            
get return this.fileSystemWatcher.EnableRaisingEvents; }
            
set this.fileSystemWatcher.EnableRaisingEvents = value; }
        }

        
//Mio evento Created
        
public event FileSystemEventHandler Created;

        
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {
            
//Se nessuno si è agganciato da fuori non faccio nulla.
            
if (null == this.Created)
                
return;
            
            
if (TryOpenFile(e.FullPath))
                
this.Created(sender, e);
        }

        
private bool TryOpenFile(string fullPath)
        {
            
bool opened = false;

            
//Se con 10 tentativi non riesco ad aprire il file me ne vado a casa.
            
int milliseconds = 0;
            
for (byte attempt = 0; attempt < 10; attempt++)
            {
                
try
                
{
                    milliseconds += 1000;

                    System.Threading.Thread.Sleep(milliseconds);
                    
//Provo ad aprire il file
                    
StreamReader sr = new StreamReader(fullPath);
                    
//Se sono qui significa che non sono nel catch e l'apertura
                    //è andata bene -> quindi rilascio il file
                    
sr.Dispose();

                    opened = 
true;
                    
break;
                }
                
catch (Exception ex)
                {
                    
//...
                
}
            }

            
return opened;
        }
    }

Mitch

powered by IMHO 1.3

posted @ Friday, September 22, 2006 5:38 PM | Feedback (9) |

Powered by:
Powered By Subtext Powered By ASP.NET