VB .NET - Usando a classe FileSystemWatcher
O componente FileSystemWatcher permite a você monitorar um diretório , ou árvore de diretório disparando eventos e enviando notificações sobre qualquer alteração ocorrida no diretório ou em seus arquivos. Assim, quando um arquivo ou um subdiretório é criado , deletado ou renomeado ou quando o atributo de uma pasta é alterado o FileSystemWatcher entra em ação e funciona como um 'dedo duro' informando estas ocorrências via eventos. |
Este componente pode ser muito útil em certas circunstâncias. Você pode usá-lo para monitorar se os seus dados estão sendo alterados por outra aplicação , ou se você precisas intervir devido a alguma alteração em um dos arquivos da sua aplicação. Ele funciona com o Windows NT, Windows 2000, Windows XP e Windows Server 2003.(Windows 98 e Windows Millennium ??? você ainda tem a coragem de usar...)
O componente pode ser usado para monitorar arquivos em uma máquina local, remota ou ambiente de rede local. Para usá-lo em sua aplicação você deve declarar o namespace System.IO.
Você pode criar um componente FileSystemWatcher de duas formas: Via código ou arrastando o componente da ToolBox para o seu formulário Windows , Formulário Web ou outro designer do Visual Studio e efetuando a configuração. (Não há muita diferença de desempenho ao usar uma ou outra).
A seguir uma visão das propriedades do componente quando usado em um formulário windows:
Conceitos Básicos
O código e os exemplos aqui demonstrados foram feitos usando o Visual Basic 2005 no Windows XP. Vamos lá...
Etapas para Configurar o componente da Classe FileSystemWatcher:
Criar uma instância do componente
Configurar as propriedades e métodos necessários
Criar um manipulador para os eventos do sistema de arquivos
Nota: Antes de utilizar o componente você precisa iniciar ao menos as seguintes propriedades :
Enumeração NotifyFilter
|
Outra propriedade importante é InternalBufferSize que obtém ou define o tamanho do buffer interno usado pelo componente. O tamanho padrão é 8k.
Para criar uma instância de um componente FileSystemWatcher você pode usar um dos seguintes métodos sobrecarregados do construtor da classe:
|
Volto a repetir, o componente não irá monitorar o diretório/arquivo definido até que a propriedade Path esteja definida, e que a propriedade EnableRaisingEvents seja definida como True.( O padrão é true).
Já deu para notar que o componente tem o potencial de receber uma enorme quantidade de eventos especialmente se você o tiver configurado para monitorar o tráfico de uma rede. Isto pode causar problemas pois o componente usa um buffer para gerenciar os eventos notificados que é passado para a API Win32 Application Programming Interface. Se ocorrerem muitas alterações em um curto período pode ocorrer um estouro de buffer fazendo com que o componente perca os dados das alterações realizadas e desta forma irá enviar notificações em branco fazendo que ocorra um exceção (O evento Error será disparado). Você pode alterar o tamanho do buffer via propriedade InternalBufferSize mas isto pode ter um custo alto demais dependendo dos valores usados. (Para evitar o estouro do buffer use as propriedades NotifyFilter e includeSubdirectories de forma filtrar as notificações.)
Podemos usar também o método WairtForChanged para esperar até que um evento especifico ocorra e então continuar com a execução da thread. Você pode definir usar o método WaitForChanged para monitorar um diretório até que a data do último acesso mude e então iniciar o processo que deseja realizar. Você define o tipo de mudança que deseja monitorar definindo o valor da enumeração WatcherChangeType. Os valores possíveis são:
Nome |
Descrição |
All
|
A criação, exclusão, alteração ou mudança de nome de arquivo ou diretório. |
Changed | A mudança de arquivo ou diretório. Os tipos de mudança incluem: mudança de tamanho, atributos, definição de segurança, última escrita e hora do último acesso. |
Created | A criação de arquivo ou diretório. |
Deleted | A exclusão de arquivo ou diretório. |
Renamed | A mudança de nome de arquivo ou diretório |
O método WaitForChanged é síncrono e retorna um objeto do tipo WaitForChangedResult. Se a aplicação não realiza qualquer operação a não ser esperar por mudanças no caminho definido ou se você monitora operações de uma thread secundária você pode escrever um código mais simples e eficiente usando o método WaitForChanged. Ele não retorna nada até que uma alteração em um arquivo seja detectada ou o tempo definido expire.
Esta classe contém informação específica sobre o tipo de mudança ocorrida no diretório. Você pode acessar a informação tal como: Name, OldName e TimedOut neste objeto para encontrar mais informações sobre a mudança.
O código abaixo ilustra um exemplo :
' Cria um novo componente FileSystemWatcher com valores Dim tmpFsw As New FileSystemWatcher(txtPath.Text, txtFilter.Text) ' Espera no máximo 10 s para qualquer evento de arquivo. Dim resultado As WaitForChangedResult = tmpFsw.WaitForChanged(WatcherChangeTypes.All, 10000) ' Verifica se a operação excedeu o tempo definido If resultado.TimedOut Then Console.WriteLine("Já se passaram 10 segundos sem evento") Else Console.WriteLine("Evento : , resultado .Name, resultado .ChangeType.ToString()) End If |
Manipulando os eventos do sistema de arquivos
O componente FileSystemWatcher dispara quatro eventos dependendo do tipo de alteração que ocorre no diretório/arquivo na monitoração. São eles:
Para acessar estes eventos você deve definir o tratamento que chama os métodos no seu código quando a mudança ocorre. Vejamos exemplos para cada um dos eventos:
dedoDuro.Changed += New FileSystemHandler(dedoDuro_Changed) dedoDuro.Created += New FileSystemHandler(dedoDuro_Created) dedoDuro.Deleted += New FileSystemHandler(dedoDuro_Deleted) dedoDuro.Renamed += New RenamedEventHandler(dedoDuro_Renamed) |
Algumas ocorrências como copiar ou mover um arquivo não correspondem diretamente a um evento que possa ser disparado. Entretanto estas ocorrências disparam outros eventos. Assim quando você copia um arquivo o sistema dispara um evento Create no diretório no qual o arquivo foi copiado mas não dispara qualquer evento no diretório original. Ao mover um arquivo o servidor dispara dois eventos: um evento Deleted no diretório origem seguido pelo evento Created no diretório destino.
Lembre-se no entanto que os eventos não serão disparados a menos que você defina a propriedade EnableRaisingEvents como sendo igual a True. Os eventos Created,Deleted e Changed recebem um objeto FileSystemEventArgs o qual expõe duas importantes propriedades: Name (o nome do arquivo que foi criado, deletado ou alterado) e FullPath (o caminho completo).
Nota: Para para desabilitar a monitoração basta definir EnableRaisingEvents como igual a False.
Um exemplo ideal para ser mostrado usando o componente FileSystemWatcher seria a criação de uma aplicação que possa estar sempre rodando e que possa ser iniciada assim que o sistema iniciar. No Visual Studio temos este recurso a disposição através da opção de criar um novo projeto do tipo Windows Service. Infelizmente o VB2005 não possui este tipo de projeto e eu ainda não tenho o VS.NET 2.0. Fico devendo, mas em breve estarei mostrando como usar este recurso.
Vamos remediar criando uma aplicação Windows forms que irá usar o FileSystemWatcher para monitorar um diretório. Na mesma aplicação iremos criar e excluir diretórios e arquivos para receber as notificações de monitoração. Para evitar o erro de chamada ilegal de Thread que acessa o controle em outra thread iremos definir a propriedade CheckForIllegalCrossThreadCalls como sendo igual a False. Isto só é valido para a .NET Framework 2.0.
Nota: Ao tentar acessar um controle de um formulário a partir de uma nova Thread iniciada irá disparar a exceção
InvalidOperationException. Definindo CheckForIllegalCrossThreadCalls como sendo False estamos evitando a exceção.
Crie um novo projeto no VB2005 do tipo Windows Forms e inclua os controles no formulário padrão conforme figura abaixo:
Declare o namespace System.IO: Imports System.io
|
No evento Click de cada um dos botões de comando teremos código que efetuará a ação de criar e deletar arquivo e diretório. O código é dado a seguir: (Não vou comentar o código pois além de óbvio não é o objetivo do artigo.)
Private Sub btnCriaArquivo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCriaArquivo.Click Dim caminho As String = txtPath.Text Dim arquivo As String = caminho + "\" + txtArquivo.Text If Not (txtArquivo.Text = "") Then If Not File.Exists(arquivo) Then Try File.Create(arquivo) Catch CrFile As Exception MessageBox.Show(CrFile.Message, "Erro ao criar arquivo", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) End Try Else MessageBox.Show(arquivo + " Arquivo já existe", "Erro na Criação do Arquivo", MessageBoxButtons.OK, MessageBoxIcon.Stop) txtArquivo.Focus() SendKeys.Send("{HOME}+{END}") End If Else MessageBox.Show("Informe o nome do arquivo", "Erro na Criação do Arquivo", MessageBoxButtons.OK, MessageBoxIcon.Information) txtArquivo.Focus() End If End Sub Private Sub btnCriarDiretorio_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCriarDiretorio.Click Dim caminho As String = txtPath.Text Dim dir As String = caminho + "\" + txtArquivo.Text If Not (txtArquivo.Text = "") Then If Not Directory.Exists(dir) Then Try Directory.CreateDirectory(dir) Catch CrDir As Exception MessageBox.Show(CrDir.Message, "Erro a criar diretório", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) End Try Else MessageBox.Show(dir + " Diretório já existe", "Erro na Criação do Diretório", MessageBoxButtons.OK, MessageBoxIcon.Stop) txtArquivo.Focus() SendKeys.Send("{HOME}+{END}") End If Else MessageBox.Show("Informe o nome do diretório", "Erro na Criação do Diretório", MessageBoxButtons.OK, MessageBoxIcon.Information) txtArquivo.Focus() End If End Sub Private Sub btnDeletaArquivo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDeletaArquivo.Click Dim caminho As String = txtPath.Text Dim arquivo As String = caminho + "\" + txtArquivo.Text If Not (txtArquivo.Text = "") Then If File.Exists(arquivo) Then Try If File.Exists(arquivo) Then File.Delete(arquivo) Else MessageBox.Show("Arquivo não existe", "Erro na exclusão do Arquivo", MessageBoxButtons.OK, MessageBoxIcon.Stop) End If Catch DelErr As Exception MessageBox.Show(DelErr.Message, "Erro na exclusão do Arquivo", MessageBoxButtons.OK, MessageBoxIcon.Stop) End Try Else MessageBox.Show(arquivo + " Arquivo não existe", "Erro na exclusão do Arquivo", MessageBoxButtons.OK, MessageBoxIcon.Stop) txtArquivo.Focus() SendKeys.Send("{HOME}+{END}") End If Else MessageBox.Show("Informe o nome do arquivo", "Erro na exclusão do Arquivo", MessageBoxButtons.OK, MessageBoxIcon.Information) txtArquivo.Focus() End If End Sub |
No início das declarações do formulário temos que definir que o FileSystemWatcher para monitorar eventos no arquivo de sistemas para isto declaramos:
Dim WithEvents dedoDuro As New FileSystemWatcher()A seguir no evento Load do formulário vamos definir os valores para monitoração do componente:
Private
Sub frmfsw_Load(ByVal
sender As System.Object, ByVal e
As System.EventArgs) Handles
MyBase.Load dedoDuro.Path
= txtPath.Text dedoDuro.Filter = txtFilter.Text
'
Monitora somente arquivos DLL ' Permite a notificação de eventos CheckForIllegalCrossThreadCalls =
False ' <<<--- atenção sem esta declaração vai ocorrer uma
exceção |
Agora é só
criar os tratadores para cada evento que desejamos monitorar , para facilitar a
leitura eu reuni os eventos em um código onde estou tratando os 3 eventos:
Private Sub dedoDuro_All(ByVal
sender As
Object,
ByVal e
As FileSystemEventArgs) Handles dedoDuro.Changed,
dedoDuro.Created, dedoDuro.Deleted txtInfo.Text += "Arquivo alterado: " & e.FullPath & " - " & "Evento : " & e.ChangeType & "" & Microsoft.VisualBasic.Chr(10) & "" End Sub |
Mas se você desejar pode criar um para cada evento declarando cada um da seguinte forma:
Protected
Sub dedoDuro_Created(ByVal
sender As
Object,
ByVal e
As FileSystemEventArgs) txtInfo.Text += "ChangeType :: " + e.ChangeType.ToString + "" & Microsoft.VisualBasic.Chr(10) & "FullPath ::" + e.FullPath.ToString + "" & Microsoft.VisualBasic.Chr(10) & "" & Microsoft.VisualBasic.Chr(10) & "" End Sub Protected Sub dedoDuro_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs)txtInfo.Text += "ChangeType :: " + e.ChangeType.ToString + "" & Microsoft.VisualBasic.Chr(10) & "FullPath ::" + e.FullPath.ToString + "" & Microsoft.VisualBasic.Chr(10) & "" & Microsoft.VisualBasic.Chr(10) & "" End Sub Protected Sub dedoDuro_Deleted(ByVal sender As Object, ByVal e As FileSystemEventArgs)txtInfo.Text += "ChangeType :: " + e.ChangeType.ToString + "" & Microsoft.VisualBasic.Chr(10) & "FullPath ::" + e.FullPath.ToString + "" & Microsoft.VisualBasic.Chr(10) & "" & Microsoft.VisualBasic.Chr(10) & "" End Sub Protected Sub dedoDuro_Renamed(ByVal sender As Object, ByVal e As RenamedEventArgs)txtInfo.Text += "ChangeType :: " + e.ChangeType.ToString + "" & Microsoft.VisualBasic.Chr(10) & "FullPath ::" + e.FullPath.ToString + "" & Microsoft.VisualBasic.Chr(10) & "Old FileName :: " + e.OldName.ToString + "" & Microsoft.VisualBasic.Chr(10) & "" & Microsoft.VisualBasic.Chr(10) & "" End Sub |
Executando o projeto e efetuando algumas operações temos:
Onde o número ao lado de Evento é
recebido da propriedade ChangeType que indica qual o tipo de evento
ocorreu: 1- Criar Por isto gerenciamos os 3 eventos usando um tratador de evento único.
|
Enfim, o componente FileSystemWatcher , quando bem usado oferece recursos que antes exigiram um esforço considerável para obter o mesmo resultado. Use mas não abuse...
Pegue o projeto completo aqui: dedoDuro.zip
Aguarde em
breve mais artigos sobre os novos recursos VB.NET 2005. Até
breve...
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
|
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#