FileSystemWatcher monitora um diretório e dispara um evento quando um arquivo dentro dele é criado, excluído, renomeado ou gravado. O sistema operacional detecta cada alteração, e a classe é a camada do .NET que repassa essa alteração para nós.
Filter define de quais arquivos recebemos aviso, e NotifyFilter define os tipos de alteração. Nenhum evento é disparado enquanto EnableRaisingEvents não for definido como true.
Dois comportamentos pegam muita gente de surpresa quando o FileSystemWatcher começa a rodar: salvar um arquivo uma única vez pode gerar três eventos, e um diretório movimentado pode gerar menos eventos do que deveria.
O que é o FileSystemWatcher em C#?
FileSystemWatcher é uma classe do namespace System.IO que monitora um diretório e dispara um evento sempre que algo dentro dele muda. Ela não fica consultando o disco: o sistema operacional informa cada alteração, e a classe a entrega para nós.
Três configurações decidem do que somos avisados. Path é o diretório a monitorar. Filter restringe o monitoramento a um padrão de arquivo, como *.txt. NotifyFilter decide quais tipos de alteração contam, do nome de um arquivo até as configurações de segurança dele.
A classe dispara cinco eventos: Created, Deleted, Changed, Renamed e Error. Nenhum deles é disparado até definirmos EnableRaisingEvents como true.
O FileSystemWatcher funciona em um disco local ou em um compartilhamento de rede, e passa a monitorar também os subdiretórios quando ativamos essa opção com IncludeSubdirectories. Ele nunca nos diz quem fez a alteração nem o que o arquivo contém agora, apenas que um caminho que nos interessa apareceu, sumiu, foi renomeado ou foi gravado.
Vejamos como configurar o FileSystemWatcher no projeto:
public class TextFileManager
{
private readonly FileSystemWatcher _fileSystemWatcher;
public TextFileManager(string rootDirectory)
{
_fileSystemWatcher = new FileSystemWatcher(rootDirectory);
}
}
Podemos inicializar FileSystemWatcher simplesmente passando o caminho que queremos monitorar. Ao longo deste artigo, vamos usar a classe TextFileManager para configurar a instância de FileSystemWatcher e trabalhar com ela. Claro, você sempre pode baixar o nosso código-fonte para ver a implementação completa.
O código de exemplo provoca os eventos com chamadas comuns, como File.WriteAllLines(), File.Move() e File.Delete(), e o nosso artigo sobre as classes File e Directory explica essas chamadas em detalhes.
Como filtrar as notificações do FileSystemWatcher?
Duas propriedades fazem a filtragem, e cada uma responde a uma pergunta diferente. Filter decide quais arquivos monitoramos. NotifyFilter decide quais alterações nesses arquivos chegam até nós.
Filter recebe um único padrão com curingas, e o valor padrão é *, que monitora todos os arquivos. Defini-la como *.txt limita o FileSystemWatcher a arquivos de texto. Para vários padrões ao mesmo tempo, existe Filters, uma coleção, de modo que uma única instância pode monitorar *.txt e *.log sem precisarmos criar uma segunda.
NotifyFilter recebe uma combinação de valores de NotifyFilters feita com OR bit a bit, e já vem configurada: o valor padrão é a combinação de LastWrite, FileName e DirectoryName. Acrescentar Size ou Attributes amplia o monitoramento, e deixar um valor de fora o restringe.
Um monitoramento restrito também é o mais seguro. Cada notificação que o FileSystemWatcher aceita ocupa espaço em um buffer interno de tamanho fixo, então cada valor extra de NotifyFilters e cada subdiretório extra tornam mais provável um estouro e, com ele, a perda de um evento.
Filter
Filter é uma propriedade de FileSystemWatcher que nos permite monitorar arquivos específicos informando um padrão de arquivo. O valor padrão de Filter é *, que monitora todos os arquivos, e *.* ou uma string vazia têm o mesmo efeito.
Vamos configurar o tipo de arquivo que queremos monitorar:
private void ConfigureFilters()
{
_fileSystemWatcher.Filter = "*.txt";
}
Agora _fileSystemWatcher monitora apenas alterações em arquivos de texto.
Definir Filter coloca o padrão na coleção Filters, então adicionar *.log a Filters depois faz com que a mesma instância informe alterações em arquivos de log, além dos arquivos de texto.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
NotifyFilter
NotifyFilter é outra propriedade de FileSystemWatcher que nos permite especificar e monitorar alterações específicas no sistema de arquivos. Podemos configurar NotifyFilter para escutar vários tipos de alteração usando o operador OR bit a bit ("|"). Por padrão, o valor de NotifyFilter é uma combinação OR bit a bit de LastWrite, DirectoryName e FileName, mas podemos ampliá-la:
private void ConfigureFilters()
{
...
_fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes
| NotifyFilters.CreationTime
| NotifyFilters.DirectoryName
| NotifyFilters.FileName
| NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.Security
| NotifyFilters.Size;
}
Combinando valores de NotifyFilters, ampliamos o leque de alterações monitoradas. A enumeração tem oito valores, e a última coluna mostra quais três já fazem parte do valor padrão:
Valor de NotifyFilters | Alteração que informa | Faz parte do padrão? |
|---|---|---|
FileName | O nome do arquivo | Sim |
DirectoryName | O nome do diretório | Sim |
LastWrite | A data da última gravação no arquivo ou na pasta | Sim |
Attributes | Os atributos do arquivo ou da pasta | Não |
Size | O tamanho do arquivo ou da pasta | Não |
LastAccess | A data da última abertura do arquivo ou da pasta | Não |
CreationTime | A hora de criação do arquivo ou da pasta | Não |
Security | As configurações de segurança do arquivo ou da pasta | Não |
Também podemos monitorar alterações dentro de subdiretórios com a propriedade IncludeSubdirectories:
private void ConfigureFilters()
{
...
_fileSystemWatcher.IncludeSubdirectories = true;
...
}
Quais eventos o FileSystemWatcher dispara?
FileSystemWatcher tem 5 tipos de evento diferentes que podemos usar. São eles: Created, Deleted, Changed, Renamed e Error.
Os cinco manipuladores a seguir vêm da classe TextFileManager do exemplo, e nenhum deles entra em ação enquanto EnableRaisingEvents não for definido, algo que o último trecho de código desta seção faz.
Created
Podemos configurá-lo para disparar um evento na criação de um arquivo ou de um diretório:
private void ConfigureEvents()
{
_fileSystemWatcher.Created += HandleCreated;
}
private void HandleCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Create: {e.FullPath}");
}
Deleted
Podemos configurá-lo para disparar um evento na exclusão de um arquivo ou diretório:
private void ConfigureEvents()
{
_fileSystemWatcher.Deleted += HandleDeleted;
...
}
private void HandleDeleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Delete: {e.FullPath}");
}
Changed
Podemos configurá-lo para disparar um evento quando há uma alteração no tamanho, nos atributos de sistema, na última atualização, na hora do último acesso ou nas permissões de um arquivo ou diretório:
private void ConfigureEvents()
{
_fileSystemWatcher.Changed += HandleChanged;
...
}
private void HandleChanged(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Change: {Enum.GetName(e.ChangeType)} {e.FullPath}");
}
Os manipuladores de eventos de Created, Deleted e Changed têm assinaturas de método parecidas.
Renamed
Podemos usá-lo para disparar um evento quando o nome de um arquivo ou de um diretório muda:
private void ConfigureEvents()
{
_fileSystemWatcher.Renamed += HandleRenamed;
...
}
private void HandleRenamed(object sender, RenamedEventArgs e)
{
Console.WriteLine($"Rename: {e.OldName} => {e.Name}");
}
Os manipuladores do evento Renamed têm uma assinatura de método diferente da dos manipuladores de Created, Deleted e Changed. O segundo argumento, do tipo RenamedEventArgs, contém as propriedades com o nome antigo e o nome novo do arquivo ou diretório afetado.
Error
Por diversos motivos, FileSystemWatcher pode chegar a um estado em que não consegue mais monitorar as alterações do sistema de arquivos no caminho especificado. Para disparar um evento nessa situação, usamos um manipulador para o evento Error:
private void ConfigureEvents()
{
_fileSystemWatcher.Error += HandleError;
...
}
private void HandleError(object sender, ErrorEventArgs e)
{
Console.WriteLine($"Error: {e.GetException().Message}");
}
Por fim, para que _fileSystemWatcher dispare eventos, precisamos definir a propriedade EnableRaisingEvents como true:
private void ConfigureEvents()
{
...
_fileSystemWatcher.EnableRaisingEvents = true;
}
Para começar o monitoramento, chamamos ConfigureFilters() e depois ConfigureEvents() no fim do construtor de TextFileManager, logo depois de criar o FileSystemWatcher.
Os eventos de FileSystemWatcher são disparados conforme as alterações correspondentes no sistema de arquivos dentro do caminho monitorado e de acordo com os filtros. Aqui estão os cinco eventos juntos, com o argumento que cada manipulador recebe:
| Evento | Argumento do manipulador | Disparado quando |
|---|---|---|
Created | FileSystemEventArgs | Um arquivo ou diretório que atende aos filtros aparece no caminho monitorado |
Deleted | FileSystemEventArgs | Um arquivo ou diretório que atende aos filtros é removido |
Changed | FileSystemEventArgs | O conteúdo, o tamanho, os atributos, os timestamps ou as configurações de segurança de um item que atende aos filtros mudam |
Renamed | RenamedEventArgs (acrescenta OldName e OldFullPath) | Um arquivo ou diretório que atende aos filtros é renomeado |
Error | ErrorEventArgs (chame GetException()) | O FileSystemWatcher perde o rastro das alterações, normalmente por causa de um estouro de buffer, ou para de monitorar porque o próprio diretório monitorado desapareceu |
Operações comuns no sistema de arquivos, como mover ou copiar, podem disparar mais de um evento.
Por que o FileSystemWatcher dispara vários eventos para uma única alteração?
Porque uma ação no sistema de arquivos raramente é uma única operação no disco. Salvar um arquivo em um editor pode gravar os bytes, atualizar o timestamp e mexer nos atributos, e cada uma dessas etapas é uma notificação separada.
A Microsoft documenta esse comportamento. Mover um arquivo entre diretórios pode disparar vários eventos Changed, junto com Created e Deleted, porque uma movimentação é composta de várias operações mais simples. Outros softwares na máquina também geram eventos, principalmente os antivírus, e o FileSystemWatcher informa essa atividade como qualquer outra.
Por isso, os manipuladores precisam tolerar repetições. Uma forma é guardar o último caminho e o timestamp que processamos e ignorar uma repetição dentro de um intervalo curto. Outra é colocar cada evento em uma fila e deixar que um único consumidor decida o que vale a pena fazer.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Created também é disparado assim que o arquivo aparece, e não quando quem está gravando nele termina, então lê-lo imediatamente pode esbarrar em um arquivo bloqueado ou gravado pela metade.

O buffer interno é o único ponto desse fluxo em que as notificações podem se perder.
A documentação do FileSystemWatcher diz isso com todas as letras: “Operações comuns no sistema de arquivos podem disparar mais de um evento.” O exemplo dado é um arquivo movido de um diretório para outro, o que, segundo a documentação, pode disparar “vários eventos OnChanged e alguns eventos OnCreated e OnDeleted“.
Antes de ler um arquivo do qual acabou de ser avisado, o manipulador pode verificar se o arquivo ainda está em uso e tentar de novo mais tarde se o processo que o grava ainda o mantiver bloqueado.
Na abordagem com fila, podemos entregar cada evento a uma fila com os Channels do .NET: o manipulador grava o evento no canal e retorna na hora, e um único consumidor lê o canal no próprio ritmo.
O caminho em FileSystemEventArgs não diz se indica um arquivo ou um diretório. Um manipulador que precisa saber isso tem de verificar por conta própria se um caminho é um arquivo ou um diretório, o que só funciona enquanto o caminho ainda existe.
Como evitar que o FileSystemWatcher perca eventos?
Impedindo que o buffer interno estoure. O sistema operacional guarda as notificações de alteração em um buffer que o FileSystemWatcher cria. O valor padrão de InternalBufferSize é 8.192 bytes, e cada notificação ocupa até 16 bytes mais o nome do arquivo. Quando o buffer enche, as notificações se perdem e o FileSystemWatcher dispara Error.
Quatro medidas evitam que ele encha. Restrinja NotifyFilter às alterações que o código realmente trata. Deixe IncludeSubdirectories desativado, a menos que seja necessário. Prefira nomes de arquivo curtos no diretório monitorado, porque o nome é a parte de cada notificação que varia de tamanho. Mantenha os manipuladores curtos e passe o trabalho lento para outra thread.
Aumente InternalBufferSize só depois dessas quatro medidas. A Microsoft documenta 4 KB como mínimo e 64 KB como máximo, e a propriedade arredonda silenciosamente para 4.096 qualquer valor abaixo de 4.096 bytes. O buffer também vem da memória não paginada, que não pode ser transferida para o disco, então um buffer maior tem um custo real para a máquina.
Para evitar o estouro do buffer:
- Reduza ao mínimo os tipos de alteração monitorados, com
NotifyFiltereIncludeSubdirectories - Monitore apenas os arquivos de interesse, especificando o padrão de arquivo. Evite monitorar arquivos com nomes longos, porque cada notificação ocupa até 16 bytes mais o nome do arquivo
- Reduza ao mínimo o código dos manipuladores de eventos
- Se ainda não houver espaço suficiente, aumente o tamanho do buffer definindo a propriedade
InternalBufferSize - Trate o evento
Error. UmaInternalBufferOverflowExceptionsignifica que notificações se perderam enquanto o FileSystemWatcher continuava funcionando, mas, seEnableRaisingEventstiver mudado parafalse, como acontece quando o diretório monitorado é excluído, recrie o FileSystemWatcher
Conclusão
FileSystemWatcher precisa de três propriedades para informar eventos do sistema de arquivos (Path, Filter e NotifyFilter), além de EnableRaisingEvents para ser ativado.
O resto do trabalho está nos manipuladores. Eles devem presumir que toda alteração pode chegar mais de uma vez e que um arquivo recém-criado ainda não está pronto para leitura, e o monitoramento deve continuar restrito para que o buffer não estoure e descarte notificações.
Um FileSystemWatcher só dispara eventos até ser descartado, então, em uma aplicação de longa duração, podemos executá-lo dentro de uma tarefa em segundo plano que fique responsável por ele enquanto o host estiver em execução.
Testado com .NET 10.0.10.