20 de julho de 2009

Criando programa para monitorar a criação de arquivos

Para auxiliar na migração de sistema de um novo Cliente da solução de ERP da ABC71, tive que montar um programa para resolver a seguinte situação: haverá um período de convivência entre o sistema que eles possuem hoje e o Omega de modo que, em certas operações, um arquivo gerado pelo sistema atual terá que ser carregado pelo Omega.

Minha primeira ideia para monitorar uma pasta no Windows foi colocar um temporizador (TTimer do Delphi ou C++ Builder). Dado um intervalo de tempo, a pasta seria varrida para determinar se havia arquivo novo a ser importado. Deparei, então, com a questão "Qual intervalo de tempo é apropriado ?". Se o intervalo for muito curto, o programa pode consumir demais os recursos do computador sem necessidade; se o tempo for muito longo, pode haver demora excessiva para a importação e, como o arquivo gerado tem ligação com faturamento do Cliente ...

Pesquisando um pouco mais, encontrei algumas funções de gerenciamento de pastas da API do Windows que ajudaram a abandonar o temporizador para resolver o problema. A função base é a FindFirstChangeNotification. A ideia dessa função é simplesmente criar um vínculo do seu programa com uma pasta do Windows de forma que você é notificado sempre que alguma mudança ocorrer nesta pasta. O código abaixo pede ao Windows que notifique mudanças na pasta "C:\temp":
HWND _WaitHandle = FindFirstChangeNotification ("C:\\TEMP", false, FILE_NOTIFY_CHANGE_FILE_NAME);

O primeiro parâmetro é o nome da pasta a ser monitorada - se tiver que monitorar mais pastas, terá que chamar a função uma vez para cada pasta. O segundo parâmetro indica se quero monitorar também as pastas que estão dentro daquela informada no primeiro parâmetro. Informei false pois não quero monitorar toda a árvore de pastas. O último parâmetro informa que tipo de mudança quero monitorar. O valor FILE_NOTIFY_CHANGE_FILE_NAME faz com que o programa seja notificado sempre que ocorrer qualquer mudança em nome de arquivo na pasta monitorada. Atenção apenas para um detalhe: "mudança de nome", aqui, inclui a criação de novos arquivos, a cópia de arquivos e a remoção de arquivos. Dê uma olhada na documentação da função para ver as outras possibilidades. No fim, a função retorna um Handle que será usado para responder ao evento.

Como é que o programa é notificado a respeito da mudança? Aqui entra uma diferença básica em relação ao princípio usado por eventos no Delphi/C++ Builder: o programa para de executar e fica em "espera" até que uma mudança seja notificada ou até que um tempo estipulado se esgote. Como no meu caso o programa faz outras coisas e não pode ficar esperando, criei uma Thread separada apenas para monitorar:
while (! Terminated)
{
VerificaPasta();
ret = WaitForSingleObject (_WaitHandle, 30000);

if (ret == WAIT_OBJECT_0 && (! Terminated))
{
VerificaPasta();
FindNextChangeNotification (_WaitHandle);
}
}

A função de sincronização WaitForSingleObject é que faz o programa aguardar até que o handle fornecido receba uma notificação ou que o tempo requisitado expire - no exemplo, esse tempo é de 30 segundos. Se esta função retornar o valor WAIT_OBJECT_0, significa que a espera terminou por causa de uma notificação de mudança (e não por timeout).

E como eu sei que a notificação foi motivada pela criação de um novo arquivo ou se um arquivo foi removido ou se simplesmente ele foi renomeado? É preciso vasculhar a pasta para determinar pois o evento em si não dá essa informação ... É o que faz a minha função VerificaPasta no exemplo acima.

Uma vez que a função de espera sai por causa de uma notificação, o programa deixa de receber novas notificações de mudança. Se quiser continuar a recebê-las, é preciso registrar novamente, dessa vez através de uma chamada à função FindNextChangeNotification, isto é, "aguarde a próxima notificação". Veja no exemplo que usei o mesmo Handle usado na chamada à função FindFirstChangeNotification.

Quando não quiser mais receber notificações, o handle tem que ser encerrado:
FindCloseChangeNotification (_WaitHandle);

Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.