2017-08-18 2 views
0

Je suis perplexe avec le comportement étrange de ne pas ReadDirectoryChangesW avec l'erreur 995. Scénario expliqué ci-dessous.ReadDirectoryChangesW échoue avec l'erreur 995 si CancelIo a été appelée avant

  1. FileHandle a été obtenu en utilisant CreateFileW.

  2. FileHandle obtenu à l'étape 1 a été utilisé dans ReadDirectoryChangesW. Il réussit et envoie la demande au serveur

  3. Sondage pendant 10 secondes et si aucune notification de modification n'a été générée par le serveur, annulez la demande de chnagenotify en utilisant cancelIo. Il envoie annuler & répond du serveur. Maintenant à nouveau modifier la modification notifier avec ReadDirectoryChangesW en utilisant le handle de fichier obtenu à l'étape 1, il échoue avec "995 - L'opération d'E/S a été interrompue en raison d'une sortie de thread ou d'une demande d'application." Aucune requête réelle n'a été envoyée au serveur par cette étape.

  4. Appelez à nouveau ReadDirectoryChangesW en utilisant le handle de fichier obtenu à l'étape 1 & et il envoie une requête au serveur.

les étapes 3,4,5 sont répétées dans une boucle & chaque ReadDirectoryChangesW alternative échoue avec 995 et immédiat suivant réussit.

Quelqu'un peut-il me dire ce qui se passe? Voici le code

void setnotify(WCHAR* _path) 
{ 
    OVERLAPPED _overlapped; 
    HANDLE  _handle; 
    char  _buffer[8192] = {0}; 
    DWORD  _bufferSize = 8192; 
    CnState _state = CN_READY; 
    DWORD  _inactivityTime = 0; 

    typedef enum State 
    { 
     CN_READY, 
     CN_REQUEST_PENDING, 
     CN_RESPONSE_RECEIVED, 
     CN_REQUEST_CANCELLED 
    } CnState; 

    _handle = CreateFileW(_path, 
      GENERIC_READ, // access 
      FILE_SHARE_READ | 
      FILE_SHARE_WRITE | 
      FILE_SHARE_DELETE, // share 
      NULL, // sec 
      OPEN_EXISTING, // disp 
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags 
      0); 
    if (_handle == INVALID_HANDLE_VALUE) 
    { 
     exit(-1); 
    } 

    memset(&_overlapped, 0, sizeof(OVERLAPPED)); 

    if (!ReadDirectoryChangesW(_handle, 
       _buffer, 
       _bufferSize, 
       true, 
       0x255, 
       NULL, 
       &_overlapped, 
       NULL)) { 

     exit(-1); 
    } else { 
     _state = CN_REQUEST_PENDING; 
     wprintf(L"Sent Change notify to Server\n"); 
    } 


    while (1) 
    { 
     if ((_state == CN_REQUEST_PENDING) && (HasOverlappedIoCompleted(&_overlapped))) { 
      wprintf(L"Response Received from Server\n"); 
      _state = CN_RESPONSE_RECEIVED; 
     } 

     if ((_state == CN_RESPONSE_RECEIVED) || (_state == CN_REQUEST_CANCELLED)) { 
      memset(&_overlapped, 0, sizeof(OVERLAPPED)); 
      _inactivityTime = 0; 
      if (!ReadDirectoryChangesW(_handle, 
         _buffer, 
         _bufferSize, 
         true, 
         255, 
         NULL, 
         &_overlapped, 
         NULL)) { 

       wprintf(L"Sent Change notify to Server Failed.\n"); 
      } else { 
       wprintf(L"Sent Change notify to Server\n"); 
       _state = CN_REQUEST_PENDING; 
      } 
     } 

     if ((_state == ChangeNotifyRequest::CN_REQUEST_PENDING) && 
       (_inactivityTime >= 5000)){ 
      if (CancelIo(_handle)) { 
       _state = CN_REQUEST_CANCELLED; 
       wprintf(L"Cancelled Pending Requests.\n"); 
      } else { 
       wprintf(L"Cancelled failed"); 
      } 

     } 

     Sleep(50); 
     _inactivityTime += 50; 

    } 
} 

ci-dessous est l'échantillon O/P:

Envoyé Notification de modification au serveur

Demandes en attente annulés.

envoyés au serveur Notification de modification

Demandes en attente annulés.

Envoyé changement au serveur notify Échec.

envoyés au serveur Notification de modification

Demandes en attente annulés.

Envoyé changement au serveur notify Échec.

envoyés au serveur Notification de modification

Demandes en attente annulés.

Envoyé changement au serveur notify Échec.

Envoyé au serveur Notification de modification

+2

Veuillez montrer votre code plutôt que de le décrire. –

+0

Merci Jonathan. Ajouté le code. – skanyal

+0

est-ce votre code actuel, sur lequel vous obtenez ce résultat? (Par exemple aucun appel GetLastError() dans le code). de toute façon vous logique pour io asynchrone est invalide. tous besoin de faire en absolu d'une autre manière – RbMm

Répondre

2

Vous commencez une opération, puis l'annuler, de sorte que son événement d'achèvement fera rapport une erreur ERROR_OPERATION_ABORTED (995). Mais, vous démarrez une nouvelle opération avant d'avoir reçu cet événement.Lorsque vous appelez CancelIo(), il s'agit simplement d'une demande d'annulation, l'opération d'origine est toujours en attente et peut prendre un certain temps avant d'être annulée (ou elle peut aboutir avant que la demande d'annulation ne soit traitée). Ainsi, vous devez toujours attendre que l'opération annulée se termine réellement, puis gérer le résultat, qu'il soit bon ou mauvais, avant de commencer l'opération suivante.

En outre, il existe deux autres bogues dans votre code.

Lorsque vous appelez le ReadDirectoryChangesW() pour la première fois, vous définissez le paramètre dwNotifyFilter sur 0x255, ce qui est incorrect. Vous demandez efficacement que ces bits de filtre:

FILE_NOTIFY_CHANGE_FILE_NAME 
FILE_NOTIFY_CHANGE_ATTRIBUTES 
FILE_NOTIFY_CHANGE_LAST_WRITE 
FILE_NOTIFY_CHANGE_CREATION 

Les appels suivants sont les moteurs dwNotifFilter-255 à la place, qui demande effectivement ces bits de filtre:

FILE_NOTIFY_CHANGE_FILE_NAME 
FILE_NOTIFY_CHANGE_DIR_NAME 
FILE_NOTIFY_CHANGE_ATTRIBUTES 
FILE_NOTIFY_CHANGE_SIZE 
FILE_NOTIFY_CHANGE_LAST_WRITE 
FILE_NOTIFY_CHANGE_LAST_ACCESS 
FILE_NOTIFY_CHANGE_CREATION 

Ainsi, votre filtrage est incompatible. Vous ne devriez vraiment pas utiliser des "nombres magiques" en premier lieu. L'API Win32 a des constantes #define pour les indicateurs disponibles, vous devriez les utiliser comme ils sont destinés. Enfin, vous n'associez pas un objet Event de CreateEvent() à la structure OVERLAPPED. Cette exigence est clairement indiquée dans la documentation ReadDirectoryChangesW() lorsque vous n'utilisez pas un rappel de fin d'E/S ou de complétion d'E/S.

Essayez quelque chose comme ceci:

void setnotify(WCHAR* _path) 
{ 
    typedef enum State 
    { 
     CN_READY, 
     CN_REQUEST_PENDING, 
     CN_REQUEST_COMPLETE 
    } CnState; 

    OVERLAPPED _overlapped = {0}; 
    HANDLE  _handle; 
    char  _buffer[8192]; 
    DWORD  _bufferSize; 
    CnState  _state = CN_READY; 
    DWORD  _inactivityTime; 
    const DWORD _filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION; 

    _handle = CreateFileW(_path, 
      GENERIC_READ, // access 
      FILE_SHARE_READ | 
      FILE_SHARE_WRITE | 
      FILE_SHARE_DELETE, // share 
      NULL, // sec 
      OPEN_EXISTING, // disp 
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags 
      0); 
    if (_handle == INVALID_HANDLE_VALUE) 
    { 
     wprintf(L"Opening Server failed. Error: %u\n", GetLastError()); 
     exit(-1); 
    } 

    _overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
    if (_overlapped.hEvent == NULL) 
    { 
     wprintf(L"Creating Overlapped Event failed. Error: %u\n", GetLastError()); 
     exit(-1); 
    } 

    do 
    { 
     switch (_state) 
     { 
      case CN_READY: 
      { 
       _bufferSize = 0; 
       _inactivityTime = 0; 

       if (!ReadDirectoryChangesW(_handle, 
         _buffer, 
         sizeof(_buffer), 
         TRUE, 
         _filter, 
         &_bufferSize, 
         &_overlapped, 
         NULL)) 
       { 
        wprintf(L"Requesting change notify from Server failed. Error: %u\n", GetLastError()); 
        exit(-1); 
       } 

       _state = CN_REQUEST_PENDING; 
       wprintf(L"Change notify requested from Server\n"); 

       break; 
      } 

      case CN_REQUEST_PENDING: 
      { 
       if (HasOverlappedIoCompleted(&_overlapped)) 
       { 
        _state = CN_REQUEST_COMPLETE; 
       } 
       else if (_inactivityTime >= 5000) 
       { 
        if (CancelIo(_handle)) 
        { 
         _state = CN_REQUEST_COMPLETE; 
         wprintf(L"No response in 5 seconds. Cancelling pending request\n"); 
        } 
        else 
         wprintf(L"No response in 5 seconds. Cancelling pending request failed. Error: %u\n", GetLastError()); 
       } 
       else 
       { 
        Sleep(50); 
        _inactivityTime += 50; 
       } 

       break; 
      } 

      case CN_REQUEST_COMPLETE: 
      { 
       if (GetOverlappedResult(_handle, &_overlapped, &_bufferSize, TRUE)) 
       { 
        wprintf(L"Response received from Server\n"); 
        // use _buffer up to _bufferSize bytes as needed... 
       } 
       else if (GetLastError() == ERROR_OPERATION_ABORTED) 
       { 
        wprintf(L"Pending request cancelled\n"); 
       } 
       else 
       { 
        wprintf(L"Change notify from Server failed. Error: %u\n", GetLastError()); 
        // handle error as needed... 
       } 

       _state = CN_READY: 
       break; 
      } 
     } 
    } 
} 

Cependant, si vous ne comptez pas utiliser un E/S achèvement Port ou E/S rappel d'achèvement, vous pouvez grandement simplifier le code en utilisant le fait que vous pouvez plus attendre efficacement sur le résultat OVERLAPPED en attente sur l'objet de l'événement à signaler, sans avoir à demander l'état OVERLAPPED dans une boucle du tout:

void setnotify(WCHAR* _path) 
{ 
    OVERLAPPED _overlapped = {0}; 
    HANDLE  _handle; 
    char  _buffer[8192]; 
    DWORD  _bufferSize; 
    const DWORD _filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION; 

    _handle = CreateFileW(_path, 
      GENERIC_READ, // access 
      FILE_SHARE_READ | 
      FILE_SHARE_WRITE | 
      FILE_SHARE_DELETE, // share 
      NULL, // sec 
      OPEN_EXISTING, // disp 
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags 
      0); 
    if (_handle == INVALID_HANDLE_VALUE) 
    { 
     wprintf(L"Opening Server failed. Error: %u\n", GetLastError()); 
     exit(-1); 
    } 

    _overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
    if (_overlapped.hEvent == NULL) 
    { 
     wprintf(L"Creating Overlapped Event failed. Error: %u\n", GetLastError()); 
     exit(-1); 
    } 

    do 
    { 
     _bufferSize = 0; 

     if (!ReadDirectoryChangesW(_handle, 
      _buffer, 
      sizeof(_buffer), 
      TRUE, 
      _filter, 
      &_bufferSize, 
      &_overlapped, 
      NULL)) 
     { 
      wprintf(L"Requesting change notify from Server failed. Error: %u\n", GetLastError()); 
      exit(-1); 
     } 

     wprintf(L"Change notify requested from Server\n"); 

     // alternatively, use GetOverlappedResultEx() with a timeout 
     // instead of WaitForSingleObject() and GetOverlappedResult() 
     // separately... 

     if (WaitForSingleObject(_overlapped.hEvent, 5000) == WAIT_TIMEOUT) 
     { 
      if (CancelIo(_handle)) 
       wprintf(L"No response in 5 seconds. Cancelling pending request\n"); 
      else 
       wprintf(L"No response in 5 seconds. Cancelling pending request failed. Error: %u\n", GetLastError()); 
     } 

     if (GetOverlappedResult(_handle, &_overlapped, &_bufferSize, TRUE)) 
     { 
      wprintf(L"Response received from Server\n"); 
      // use _buffer up to _bufferSize bytes as needed... 
     } 
     else if (GetLastError() == ERROR_OPERATION_ABORTED) 
     { 
      wprintf(L"Pending request cancelled\n"); 
     } 
     else 
     { 
      wprintf(L"Change notify from Server failed. Error: %u\n", GetLastError()); 
      // handle error as needed... 
     } 
    } 
    while (true); 
} 

voir aussi my earlier answer-a similar question, ce qui explique quelques autres pièges que vous devez savoir lorsque vous utilisez ReadDirectoryChangesW(), en particulier la gestion de l'erreur ERROR_NOTIFY_ENUM_DIR.

+0

Merci Remy. Comment pourrais-je attendre que le Cancelo() se termine réellement avant d'envoyer ReadDirectoryChangesW (pour éviter l'échec que je vois). C'était surprenant pour moi parce que chaque fois que j'exécute ce programme, ReadDirectoryChangesW première fois après cancelIo succeds (ligne 3 de la sortie), mais après que chaque demande échoue – skanyal

+0

@skanyal se débarrasser de votre logique 'CN_REQUEST_CANCELLED', rester dans l'état 'CN_REQUEST_PENDING' jusqu'à ce que' HasOverlappedIoCompleted() 'indique autrement, puis gérer le résultat de l'opération terminée si nécessaire avant d'appeler' ReadDirectoryChangesW() 'à nouveau. –

+0

Toujours la même erreur même après le traitement cancelIo Resp: Envoyé Modifier notifier au serveur. Demandes en attente annulées. Réponse reçue du serveur. Envoyé Modifier notifier au serveur. Demandes en attente annulées. . Réponse reçue du serveur. La notification de modification envoyée au serveur a échoué. Envoyé Modifier notifier au serveur. Demandes en attente annulées. Réponse reçue du serveur. La notification de modification envoyée au serveur a échoué. Envoyé Modifier notifier au serveur. – skanyal