C# - Usando WaitForChanged para receber notificações


Hoje veremos como receber notificações de eventos em operações com o sistema de arquivos de forma síncrona na linguagem C#.

Vamos supor que você precisa ser notificado quando um determinado evento ocorrer no seu sistema de arquivos, seriam eventos como o renomear um arquivo ou diretório, aumentar ou diminuir o tamanho de um arquivo, excluir um arquivo ou diretório, criar um arquivo ou diretório, ou mesmo a alteração dos atributos de um arquivo ou diretório.

Ocorre que esta notificação deve ocorrer síncrona, ou seja, a sua aplicação não pode continuar a execução a menos que uma ação específica ocorrer em um arquivo ou diretório.

Como você faria para receber esta notificação de forma síncrona ?

Para este cenário específico temos a classe FileSystemWatcher e nesta classe o método WaitForChanged que pode ser chamado para esperar de forma síncrona a notificação de um evento.

A sintaxe mais usada é a sobrecarga :  WaitForChanged(WatcherChangeTypes, Int32)

Que retorna uma estrutura que contém informações específicas sobre a alteração ocorrida, considerando o tipo de alteração que você deseja monitorar e o tempo (em milissegundos) de espera antes do tempo limite.

Esse método aguarda até que ocorra uma alteração ou tenha atingido o tempo limite. Um valor de -1 para o parâmetro timeout significa aguardar indefinidamente.

O retorno deste método é um tipo WaitForChangeResult  que contém informações especificas sobre a alteração ocorrida.

Exemplo:

static void EsperarPelaAcao(FileSystemWatcher watcher)
{
    Console.WriteLine("Aguardando 10 segundos para que o arquivo seja criado");

     WaitForChangedResult resultado = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);

     if (resultado.TimedOut)
            Console.WriteLine("Tempo esgotado...");
      else
            Console.WriteLine($"ChangeType = {resultado.ChangeType}");
 }

 

O segundo parâmetro opcional para o método WaitForChanged especifica o tempo em milissegundos de espera antes que a solicitação expire.

Tenha cuidado ao usar o método WaitForChanged na thread principal de um programa Console ou na thread de interface do usuário de um aplicativo Windows Forms. Como esse método é bloqueado até que a alteração ocorra ou o valor de tempo limite seja atingido, nenhum outro processamento ocorrerá na thread que o chama. Em um aplicativo Windows Forms, a interface do usuário não responderá se essa chamada ocorrer no thread de interface do usuário em vez de em um thread de trabalho.

Criando um arquivo e aguardando a notificação

Como exemplo prático veremos um programa Console onde o método AguardaCriacaoArquivo(caminho, arquivo) recebe o caminho para criar o arquivo e o nome do arquivo.

A seguir usaremos o método  WaitForChanged(WatcherChangeTypes.Created, 10000); onde vamos esperar 10 segundos para receber a notificação exibindo o resultado.

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

namespace CShp_WaitForChanged
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("Criando arquivo ZIP... Aguarde...");
            var caminho = @"c:\dados\txt\";
            var arquivo = "Teste.txt";
            AguardaCriacaoArquivo(caminho, arquivo);
            Console.ReadKey();
        }

        public static void AguardaCriacaoArquivo(string path, string nomeArquivo)
        {
            if (string.IsNullOrWhiteSpace(path))
                throw new ArgumentNullException(nameof(path));

            if (string.IsNullOrWhiteSpace(nomeArquivo))
                throw new ArgumentNullException(nameof(nomeArquivo));

            FileSystemWatcher fsw = null;

            try
            {
                fsw = new FileSystemWatcher();
                string[] data = new string[] { path, nomeArquivo };
                fsw.Path = path;
                fsw.Filter = nomeArquivo;

                fsw.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                | NotifyFilters.FileName | NotifyFilters.DirectoryName;

                Task work = Task.Run(() =>
                {
                    try
                    {
                        Thread.Sleep(1000);
                        if (data.Length == 2)
                        {
                            string dataPath = data[0];
                            string arquivoDados = path + data[1];
                            Console.WriteLine($"Criando {arquivoDados} na tarefa...");
                            FileStream fileStream = File.Create(arquivoDados);
                            fileStream.Close();
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.ToString());
                    }
                });

            
                WaitForChangedResult resultado =  fsw.WaitForChanged(WatcherChangeTypes.Created, 10000);

                Console.WriteLine($"Arquivo {resultado.Name} criado em {path}.\nStatus :: {resultado.ChangeType}");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            finally
            {
                File.Delete(nomeArquivo);
                fsw?.Dispose();
            }
        }
    }
}

Resultado:

O método WaitForChanged retorna uma estrutura WaitForChangedResult que contém as propriedades listadas abaixo:

Propriedade Descrição
ChangeType Lista o tipo de mudança que ocorreu. Essa alteração é retornada como uma enumeração WatcherChangeTypes.
Os valores desta enumeração podem possivelmente ser combinados com OR.
Name Contém o nome do arquivo ou diretório que foi alterado. Se o arquivo ou diretório foi renomeado, esta propriedade retorna o nome alterado. Seu valor é definido como nulo se a chamada do método de operação atingir o tempo limite.
OldName O nome original do arquivo ou diretório modificado. Se este arquivo ou diretório não foi renomeado, esta propriedade irá retorna o mesmo valor da propriedade Name. Seu valor é definido como nulo se a chamada do método de operação atingir o tempo limite.
TimedOut Contém um booleano indicando se o método WaitForChanged atingiu o tempo limite (verdadeiro) ou não (falso)

A enumeração NotifyFilters permite que você especifique os tipos de arquivos ou pastas para observar, conforme mostrado a seguir:

Valor Definição
FileName Nome do arquivo
DirectoryName Nome do diretório
Attributes Atributos dos arquivos/diretórios
Size Tamanho do arquivo/diretório
LastWrite Última data da escrita no arquivo ou diretório
LastAccess Última vez que o arquivo ou pasta foi aberto
CreationTime A hora da criação do arquivo/diretório
Security As configurações de segurança

Pegue o código do projeto aqui: CShp_WaitForChanged.zip

"Vigiai, estai firmes na fé; portai-vos varonilmente, e fortalecei-vos.
Todas as vossas coisas sejam feitas com amor."
1 Coríntios 16:13,14

Referências:


José Carlos Macoratti