Inicio > .NET Framework, Apunte, C# > Monitorear el cambio de archivos en un directorio… versión .NET

Monitorear el cambio de archivos en un directorio… versión .NET


En mi última entrada mostré cómo se puede monitorear un directorio para saber cuándo se ha agregado algún archivo, modificado, leído, eliminado, etc., utilizando C++ y el API de Windows. Bien, ahora veremos como hacer eso mismo en .NET.

El .NET Framework nos provee la clase System.IO.FileSystemWatcher para ello. No cabe duda, esta clase es un simple envoltorio al API de Windows ReadDirectoryChangesW expuesta en la entrada antes mencionada. Los conceptos, pues, ya los tenemos, así que veamos cómo se hace à la .NET.

En primer lugar, nos construimos un objeto FileSystemWatcher, al que le podemos pasar hasta dos parámetros: el directorio que queremos monitorear, y un filtro para especificar que solo monitorearemos aquellos archivos que concuerden (por ejemplo, “*.txt” montoreará solo los archivos que tengan extensión txt; mientras que “a*.txt” monitoreará los archivos de texto que comiencen con “a”). Si usamos el constructor por default, entonces podremos especificar ambos valores a través de las propiedades Path y Filter, respectivamente:

FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = @"C:\users\fgomez\";
watcher.Filter = "*.txt";
watcher.IncludeSubdirectories = false;

Podemos especificar si queremos incluir los subdirectorios a través de la propiedad IncludeSubdirectories.

Ahora bien, aquí la cosa se pone interesante. Al igual que con ReadDirectoryChangesW, tenemos que especificarle a nuestro objeto qué eventos son los que nos interesa atrapar. Esto lo hacemos a través de la propiedad NotifyFilters. Esta propiedad espera una enumeración de tipo NotifyFilters (que por estar marcada con el atributo FlagsAttribute, podemos mezclar). Los valores disponibles son:

  • FileName.- monitorea cambios en el nombre de algún archivo.
  • DirectoryName.- monitorea cambios en el nombre de algún directorio.
  • Attributes.- monitorea cambios en los atributos de algún archivo o directorio.
  • Size.- monitorea cambios en el tamaño de algún archivo.
  • LastWrite.- monitorea cambios de escritura en algún archivo.
  • LastAccess.- monitorea cuándo se ha leído algún archivo.
  • CreationTime.- monitorea cuándo se ha creado algún archivo.
  • Security.- monitorea cambios en la directiva de seguridad de algún archivo.
    Así pues, para monitorear cuando un archivo cambia de nombre, podemos hacer:
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = @"C:\users\fgomez\";
    watcher.Filter = "*.txt";
    watcher.IncludeSubdirectories = false;
    watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.FileName;
    

Bien, ahora solo nos queda esperar a que se produzca algún cambio. Para ello tenemos dos formas de hacerlo: de forma síncrona y de forma asíncrona.

Si lo hacemos de forma síncrona, la ejecución de nuestro programa parará hasta que se produzca algún cambio. Para ello, basta invocar al método WaitForChanged, que nos devuelve un objeto de tipo WaitForChangedResult, con información sobre el archivo que fue modificado y el tipo de cambio que se suscitó. Se le pasa como parámetro una enumeración de tipo WatcherChangeTypes, que indica si queremos esperar a que un archivo cambie, se cree, se modifique o elimine (por ejemplo, WatcherChangeTypes.Created | WatcherChangeTypes.Deleted nos indicará cuando un archivo se haya creado o modificado). Opcionalmente, podemos pasar un valor que indique cuántos segundos el método esperará antes de lanzar un “time-out” y regresar el control al programa. Sirva el siguiente programita de ejemplo.

using System;
using System.IO;

class Program
{
  static void Main(string[] args)
  {
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = @"C:\users\fgomez\";
    watcher.Filter = "*.txt";
    watcher.IncludeSubdirectories = false;
    WaitForChangedResult result = 
          watcher.WaitForChanged(WatcherChangeTypes.Created);

    Console.WriteLine("Se ha creado el archivo '{0}'.", result.Name);

    Console.ReadKey(true);
  }
}

Cuando ejecuto este programa y creo un archivo de texto nuevo desde el explorador de Windows, obtengo el siguiente resultado:

Se ha creado el archivo 'Nuevo documento de texto.txt'.

Ahora bien, si queremos que el programa haga alguna otra cosa mientras esperamos a que ocurra el cambio, entonces tenemos que hacerlo de forma asíncrona. Para ello, FileSystemWatcher tiene varios eventos a los que nos podemos subscribir: Changed, Created, Deleted, Error y Renamed, si queremos monitorear cuando un archivo se cambia, se crea, se elimina, contiene un error o se renombra, respectivamente. A excepción del evento Error, los demás eventos esperan un delegado del tipo FileSystemEventHandler, cuyo segundo parámetro tiene que ser de tipo FileSystemEventArgs, donde se nos proporciona los datos del archivo que fue cambiado. Error, en cambio, espera un delegado ErrorEventHandler, cuyo segundo parámetro de tipo ErrorEventArgs, nos proporciona información sobre el error que se generó.

Una vez que nos hemos subscrito a los eventos que nos interesan establecemos la propiedad EnableRaisingEvents a true y a partir de ahí comienza el monitoreo. Obviamente si después la establecemos a false dejaremos de monitorear.

Para ilustrar esto, he aquí otro programita de ejemplo. En éste esperamos a que se cree algún archivo, y mientras simplemente escribimos una frase y dormimos la aplicación por un segundo. Cuando se crea algún archivo terminamos la aplicación.

using System;
using System.IO;
using System.Threading;

class Program
{
  static bool _continue = true;

  static void Main(string[] args)
  {
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = @"C:\users\fgomez\";
    watcher.Filter = "*.txt";
    watcher.IncludeSubdirectories = false;
    watcher.Created += new FileSystemEventHandler(OnCreated);
    watcher.EnableRaisingEvents = true;

    while (_continue)
    {
      Console.WriteLine("Monitoreando... ");
      Thread.Sleep(1000);
    }

    Console.ReadKey(true);
  }

  static void OnCreated(object sender, FileSystemEventArgs e)
  {
    Console.WriteLine("Se ha creado el archivo '{0}'", e.Name);
    _continue = false;
  }
}

Al ejecutarlo, obtengo la siguiente salida:

Monitoreando...
Monitoreando...
Monitoreando...
Monitoreando...
Monitoreando...
Monitoreando...
Monitoreando...
Se ha creado el archivo 'Nuevo documento de texto.txt'

Bueno, hemos visto que .NET presenta una forma más elegante de monitorear archivos que el API de Windows. Y mucho más sencillo de manejar. Así que ya puedes comenzar a implementar esto en tus programas, que ya ves lo fácil que resultó.

Hasta la próxima, que hoy ya escribí bastante y me tengo que ir a dormir. Suerte.

Anuncios
Categorías:.NET Framework, Apunte, C# Etiquetas:
  1. yercar
    diciembre 13, 2010 en 11:51 pm

    Hola!, oye de que manera puedo hacer algo asi como este monitoreo pero para bases de datos? Es decir, quiero hacer una aplicacion que detecte cualquier cambio en campos de una tabla en sql pero desde C#, esto se puede hacer? Saludos!

    • diciembre 13, 2010 en 11:56 pm

      Bueno, siempre puedes monitorear el cambio en los archivos de la base de datos (por ejemplo, los archivos MDF de SQL Server, etc). Pero no es algo muy útil. Algunas bases de datos cuentan con esa funcionalidad integrada, y tienes que usar SQL y funciones intrínsecas, no nada más C#.

      Este es el caso de SQL Server. En los siguientes enlaces a MSDN puedes darte una idea de cómo hacerlo.
      http://msdn.microsoft.com/es-es/library/bb726006.aspx
      http://msdn.microsoft.com/es-es/library/cc305322.aspx

      Espero te sean de utilidad.

      Saludos.

      • yercar
        diciembre 14, 2010 en 10:10 am

        Gracias!!!
        También estuve investigando y me encontre con SqlDependency, voy a estudiar esto y los links que me envías para armar lo que necesito. Que tengas un excelente día ;-)

  2. zzzzzooooo
    marzo 6, 2012 en 1:27 pm

    Gracias, realmente útil la información

  3. abril 10, 2012 en 7:08 am

    Hola, excelente post. Te consulto. Supongamos que tenemos la carpeta 1 y dentro de la carpeta 1 tengo la carpeta 2. Ahora bien, lo que quiero hacer es que al cortar (delete para tu sistema) se pegue en la carpeta 2 (recorda esta DENTRO de la carpeta 1); …. como puedo hacer para que el sistema me tome los 2 eventos. Porque al hacerlo con tu sistema solo me toma el cortar(delete). Te aclaro que le puse a true el de observar subdirectorios.

    Gracias

  4. Cesar
    enero 23, 2015 en 9:17 am

    estoy usando este control, pero tengo un problema:

    el programa debe hacer lo siguiente:

    – si se ha grabado un archivo en la carpeta, debe correr un proceso que usa una Aplicacion que no es multitasking, eso significa que el Filesystemwatcher debe esperar a que el programa este listo para seguir monitoreando.
    -el problema se produce cuando dos archivos se graban casi simultaneamente en la carpeta que se esta monitoreando.

    hay una solución para esto?

  5. mayo 7, 2015 en 6:44 am

    Excelente aporte!!
    Tengo una pregunta, se podría utilizar en el mismo Thread para monitorizar 2 directorios diferentes pero sobre ficheros de diferente extensióh? ¿Si es así como podría hacerlo?

    Gracias de antemano.

  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s