2017-07-22 5 views
1

J'ai une application qui doit surveiller les changements dans un répertoire particulier. J'utilise le code suivant, cependant quand je renomme un fichier par exemple, l'action FILE_ACTION_RENAMED_NEW_NAME n'est pas déclenchée. En fait, le UNDISCOVERED ACTION est déclenché ce qui entraîne un comportement inattendu? Qu'est-ce que je fais mal ici?C++ Surveillance filechange avec ReadDirectoryChangesW ne déclenche pas toutes les actions?

char Dir[] = "DIRPATH"; 
HANDLE hDir = CreateFile(
    Dir, 
    FILE_LIST_DIRECTORY, 
    FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, 
    NULL, 
    OPEN_EXISTING, 
    FILE_FLAG_BACKUP_SEMANTICS, 
    NULL); 
int nCounter = 0; 
FILE_NOTIFY_INFORMATION strFileNotifyInfo[1024]; 
FILE_NOTIFY_INFORMATION *fni = NULL; 

while (TRUE) 
{ 
    //strFileNotifyInfo = NULL; 
    DWORD dwBytesReturned = 0; 
    if (ReadDirectoryChangesW(hDir, (LPVOID)&strFileNotifyInfo, sizeof(strFileNotifyInfo), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytesReturned, NULL, NULL) == 0) 
    { 
     Exit(GetLastErrorAsString()); 
    } 
    else 
    { 
     char fileName[MAX_PATH] = ""; 
     DWORD offset = 0; 
     do { 
      fni = (FILE_NOTIFY_INFORMATION*)(&strFileNotifyInfo[offset]); 
      int ret = ::WideCharToMultiByte(CP_ACP, 0, fni->FileName, fni->FileNameLength/sizeof(WCHAR), fileName, sizeof(fileName), NULL, NULL); 
      string Test = Dir; 
      Test += "\\"; 
      Test += fileName; 
      switch (fni->Action) { 
       case FILE_ACTION_ADDED: 
        if (boost::filesystem::is_directory(Test)) { 
         cout << "Directory added: " << Dir << "\\" << fileName << endl; 
         } 
        else { 
         cout << "File added: " << Dir << "\\" << fileName << endl; 
         } 
        break; 
       case FILE_ACTION_MODIFIED: 
        if (boost::filesystem::is_directory(Test)) { 
         cout << "Directory modified: " << Dir << "\\" << fileName << endl; 
         } 
        else { 
         cout << "File modified: " << Dir << "\\" << fileName << endl; 
         } 
        break; 
       case FILE_ACTION_REMOVED: 
        if (boost::filesystem::is_directory(Test)) { 
         cout << "Directory removed: " << Dir << "\\" << fileName << endl; 
         } 
        else { 
         cout << "File removed: " << Dir << "\\" << fileName << endl; 
         } 
        break; 
       case FILE_ACTION_RENAMED_NEW_NAME: 
        if (boost::filesystem::is_directory(Test)) { 
         cout << "Directory renamend (NEW): " << Dir << "\\" << fileName << endl; 
         } 
        else { 
         cout << "File renamed (NEW): " << Dir << "\\" << fileName << endl; 
         } 
        break; 
       case FILE_ACTION_RENAMED_OLD_NAME: 
        if (boost::filesystem::is_directory(Test)) { 
         cout << "Directory renamed (OLD): " << Dir << "\\" << fileName << endl; 
         } 
        else { 
         cout << "File renamed (OLD): " << Dir << "\\" << fileName << endl; 
         } 
        break; 
       default: 
        if (boost::filesystem::is_directory(Test)) { 
         cout << "Directory UNDISCOVERED ACTION: " << Dir << "\\" << fileName << endl; 
         } 
        else { 
         cout << "File UNDISCOVERED ACTION: " << Dir << "\\" << fileName << endl; 
         } 
        break; 
       } 
      ::memset(fileName, '\0', sizeof(fileName)); 
      offset += fni->NextEntryOffset; 
      } 
     while (fni->NextEntryOffset != 0); 
     cout << "Loop: " << nCounter++ << endl; 
    } 
} 

Certains exemple de sortie sur renommer un fichier de file.txt à file2.txt sur la carte nommée: MAP:

File renamed (OLD): DIRPATH\MAP\file.txt 
Directory UNDISCOVERED ACTION: DIRPATH 
Loop: 0 
Directory modified: DIRPATH\MAP 
Loop: 1 
+0

avoir une lecture de The Old New Thing: https://blogs.msdn.microsoft.com/oldnewthing/20110812-00/?p=9913 –

+0

Notez en particulier le paragraphe à la fin de l'article que Richard a lié à: 'ReadDirectoryChangesW' peut échouer avec' ERROR_NOTIFY_ENUM_DIR' si son tampon interne manque de place. Dans ce cas, vous pouvez revenir à une boucle 'FindFirstFile' /' FindNextFile'. –

Répondre

2

FILE_NOTIFY_INFORMATION n'est pas une structure de taille fixe. Il représente un en-tête de taille fixe suivi du nom du fichier - une chaîne de taille variable.

&strFileNotifyInfo[1] pointe vers un certain décalage au milieu du nom de fichier qui suit strFileNotifyInfo[0]. Ses valeurs sont simplement des morceaux de mémoire mal interprétés remplis de caractères de nom de fichier; poubelle essentiellement aléatoire. Au lieu de cela, vous devez utiliser FILE_NOTIFY_INFORMATION::NextEntryOffset pour localiser l'instance suivante de FILE_NOTIFY_INFORMATION dans le tampon.

Votre code ressemblerait à quelque chose comme ceci:

BYTE buffer[4096]; 
ReadDirectoryChangesW(hDir, buffer, sizeof(buffer), ...); 
BYTE* p = buffer; 
for (;;) { 
    FILE_NOTIFY_INFORMATION* info = 
     reinterpret_cast<FILE_NOTIFY_INFORMATION*>(p); 

    // Work with `info` as necessary 

    if (!info->NextEntryOffset) break; // this was last entry 
    p += info->NextEntryOffset; 
} 
+0

Eh bien, j'utilise 'NextEntryOffset' pour passer à l'instance suivante de' FILE_NOTIFY_INFORMATION' à droite? voir la fin de la 'do-loop'? S'il vous plaît laissez-moi savoir si je vous ai mal compris? –

+2

'NextEntryOffset' est un décalage en octets, pas dans les éléments' FILE_NOTIFY_INFORMATION'. Cependant, 'strFileNotifyInfo [offset]' inrerprets ce dernier. En d'autres termes, le tampon que vous passez à 'ReadDirectoryChangesW' est un tableau d'octets. À son retour, il contient des en-têtes 'FILE_NOTIFY_INFORMATION' à intervalles irréguliers, avec des morceaux de texte de longueur variable entre les deux. Ce n'est pas un tableau d'éléments 'FILE_NOTIFY_INFORMATION' situés côte à côte. –

+0

Merci pour l'explication. J'ai fait des recherches et je crois comprendre ce que vous voulez dire. Cependant, comment puis-je résoudre ce problème? –