Obtendo notificações de alteração de diretório

Um aplicativo pode monitorar o conteúdo de um diretório e seus subdiretórios usando notificações de alteração. Aguardar uma notificação de alteração é semelhante a ter uma operação de leitura pendente em um diretório e, se necessário, seus subdiretórios. Quando algo muda dentro do diretório que está sendo observado, a operação de leitura é concluída. Por exemplo, um aplicativo pode usar essas funções para atualizar uma listagem de diretório sempre que um nome de arquivo dentro do diretório monitorado for alterado.

Um aplicativo pode especificar um conjunto de condições que disparam uma notificação de alteração usando a função FindFirstChangeNotification . As condições incluem alterações em nomes de arquivo, nomes de diretório, atributos, tamanho do arquivo, hora da última gravação e segurança. Essa função também retorna um identificador que pode ser aguardado usando as funções de espera. Se a condição de espera for atendida, FindNextChangeNotification poderá ser usado para fornecer um identificador de notificação para aguardar as alterações subsequentes. No entanto, essas funções não indicam a alteração real que atendeu à condição de espera.

Use FindCloseChangeNotification para fechar o identificador de notificação.

Para recuperar informações sobre a alteração específica como parte da notificação, use a função ReadDirectoryChangesW . Essa função também permite que você forneça uma rotina de conclusão.

Para controlar as alterações em um volume, consulte alterar diários.

O exemplo a seguir monitora a árvore de diretórios para alterações de nome de diretório. Ele também monitora um diretório para alterações de nome de arquivo. O exemplo usa a função FindFirstChangeNotification para criar dois identificadores de notificação e a função WaitForMultipleObjects para aguardar os identificadores. Sempre que um diretório é criado ou excluído na árvore, o exemplo deve atualizar toda a árvore de diretórios. Sempre que um arquivo é criado ou excluído no diretório, o exemplo deve atualizar o diretório.

Observação

Este exemplo simplista usa a função ExitProcess para encerramento e limpeza, mas aplicativos mais complexos sempre devem usar o gerenciamento de recursos adequado, como FindCloseChangeNotification , quando apropriado.

 

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>

void RefreshDirectory(LPTSTR);
void RefreshTree(LPTSTR);
void WatchDirectory(LPTSTR);

void _tmain(int argc, TCHAR *argv[])
{
    if(argc != 2)
    {
        _tprintf(TEXT("Usage: %s <dir>\n"), argv[0]);
        return;
    }

    WatchDirectory(argv[1]);
}

void WatchDirectory(LPTSTR lpDir)
{
   DWORD dwWaitStatus; 
   HANDLE dwChangeHandles[2]; 
   TCHAR lpDrive[4];
   TCHAR lpFile[_MAX_FNAME];
   TCHAR lpExt[_MAX_EXT];

   _tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);

   lpDrive[2] = (TCHAR)'\\';
   lpDrive[3] = (TCHAR)'\0';
 
// Watch the directory for file creation and deletion. 
 
   dwChangeHandles[0] = FindFirstChangeNotification( 
      lpDir,                         // directory to watch 
      FALSE,                         // do not watch subtree 
      FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes 
 
   if (dwChangeHandles[0] == INVALID_HANDLE_VALUE) 
   {
     printf("\n ERROR: FindFirstChangeNotification function failed.\n");
     ExitProcess(GetLastError()); 
   }
 
// Watch the subtree for directory creation and deletion. 
 
   dwChangeHandles[1] = FindFirstChangeNotification( 
      lpDrive,                       // directory to watch 
      TRUE,                          // watch the subtree 
      FILE_NOTIFY_CHANGE_DIR_NAME);  // watch dir name changes 
 
   if (dwChangeHandles[1] == INVALID_HANDLE_VALUE) 
   {
     printf("\n ERROR: FindFirstChangeNotification function failed.\n");
     ExitProcess(GetLastError()); 
   }
 

// Make a final validation check on our handles.

   if ((dwChangeHandles[0] == NULL) || (dwChangeHandles[1] == NULL))
   {
     printf("\n ERROR: Unexpected NULL from FindFirstChangeNotification.\n");
     ExitProcess(GetLastError()); 
   }

// Change notification is set. Now wait on both notification 
// handles and refresh accordingly. 
 
   while (TRUE) 
   { 
   // Wait for notification.
 
      printf("\nWaiting for notification...\n");

      dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, 
         FALSE, INFINITE); 
 
      switch (dwWaitStatus) 
      { 
         case WAIT_OBJECT_0: 
 
         // A file was created, renamed, or deleted in the directory.
         // Refresh this directory and restart the notification.
 
             RefreshDirectory(lpDir); 
             if ( FindNextChangeNotification(dwChangeHandles[0]) == FALSE )
             {
               printf("\n ERROR: FindNextChangeNotification function failed.\n");
               ExitProcess(GetLastError()); 
             }
             break; 
 
         case WAIT_OBJECT_0 + 1: 
 
         // A directory was created, renamed, or deleted.
         // Refresh the tree and restart the notification.
 
             RefreshTree(lpDrive); 
             if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE )
             {
               printf("\n ERROR: FindNextChangeNotification function failed.\n");
               ExitProcess(GetLastError()); 
             }
             break; 
 
         case WAIT_TIMEOUT:

         // A timeout occurred, this would happen if some value other 
         // than INFINITE is used in the Wait call and no changes occur.
         // In a single-threaded environment you might not want an
         // INFINITE wait.
 
            printf("\nNo changes in the timeout period.\n");
            break;

         default: 
            printf("\n ERROR: Unhandled dwWaitStatus.\n");
            ExitProcess(GetLastError());
            break;
      }
   }
}

void RefreshDirectory(LPTSTR lpDir)
{
   // This is where you might place code to refresh your
   // directory listing, but not the subtree because it
   // would not be necessary.

   _tprintf(TEXT("Directory (%s) changed.\n"), lpDir);
}

void RefreshTree(LPTSTR lpDrive)
{
   // This is where you might place code to refresh your
   // directory listing, including the subtree.

   _tprintf(TEXT("Directory tree (%s) changed.\n"), lpDrive);
}