2011-05-27 5 views
10

Exactement comme il semble, je tente asynchrone ReadDirectoryChangesW avec IO Achèvement et il ne fonctionne pas, en particulier, GetLastError renvoie à plusieurs reprises 258 (GetQueuedCompletionStatus timeout).ReadDirectoryChangesW asynchrone - GetQueuedCompletionStatus expire toujours

J'ai struct:

typedef struct dirinfo_struct 
{ 
    HANDLE hDirFH;   // directory handle 
    OVERLAPPED Overlapped; // overlapped storage 
    int len_buffer;   // buffer length 
    wchar_t* buffer;   // buffer itself 
    wchar_t* directory_name; // target name 
} dirinfo_t; 

typedef struct dirmon_struct 
{ 
    HANDLE hDirOPPort;  // handle to the IO port. 
    dirinfo_t* dirinfo;  // pointer to the struct above. 
} dirmon_t; 

pour stocker les informations pertinentes. Ceci est initialisés:

dirinfo_t* t = malloc(1*sizeof(dirinfo_t)); 
dirmon_t* d = malloc(1*sizeof(dirmon_t)); 
dirinfo_init(t); // does t->buffer = malloc(8192*sizeof(wchar_t)); 

Puis-je créer ma poignée Directory et port com:

t->hDirFH = CreateFile(L"C:\\test", 
         FILE_LIST_DIRECTORY, 
         FILE_SHARE_READ|FILE_SHARE_WRITE, 
         NULL, 
         OPEN_EXISTING, 
         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, 
         NULL); 
d->dirinfo = t; 
d->hDirOPPort = CreateIoCompletionPort(d->dirinfo->hDirFH, 
             NULL,  
             (ULONG_PTR)(d->dirinfo), 
             1); 

Ensuite, je passe ces informations par d à un nouveau thread. Maintenant, sur ledit nouveau thread j'ai:

bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, lpBytes, 
            (ULONG_PTR*)d->dirinfo,  
            lpOverlapped, 1000); 

if (bResultQ) 
{ 
    bResultR = ReadDirectoryChangesW(d->dirinfo->hDirFH, 
            (void*)d->dirinfo->buffer, 
            8192, TRUE, 
            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 | 
            FILE_NOTIFY_CHANGE_SECURITY, 
            lpReadDirBytes, 
            &d->dirinfo->Overlapped, 
            NULL); 
} 
else 
{ 
    printf("GetQueuedCompletionStatus(): Failed, "); 
    errorcode = GetLastError(); 
    printf("Error Code %d\n", errorcode); 
    Sleep(500); 
} 

Je mis cette course et je off obtenir allègrement les délais d'attente (258 erreurs) que je devrais depuis le répertoire n'a pas changé. Cependant, même si je modifie le répertoire, je reçois toujours des messages d'erreur; en d'autres termes, ces modifications ne sont pas prises en compte. Ce qui m'amène à croire que j'ai mal configuré cette configuration.

Des idées?

Avertissements:

  • Ironie du sort, ce qui finira par être converti en Python via pywin32. Cependant, jusqu'à ce que je comprenne comment cela est censé fonctionner en C, je n'y vais pas.
  • Synchrone ReadDirectoryChangesW n'est pas une option. Si aucun événement n'est déclenché, j'ai besoin que le thread sur lequel il est en attente expire afin qu'il puisse vérifier s'il devrait encore être en cours d'exécution. J'écris dans C spécifiquement, pas C++. Etc non une option non plus - Je ne veux pas continuellement comparer les listes de répertoires pour déterminer ce qui a changé.

Autres notes:

  • Le répertoire existe, cette poignée est NULL. De même pour le handle de comportement.
  • Tout est d'être passé au fil

J'ai examiné à CDirectoryChangeWatcher du projet de code, mais l'utilisation de C++ et bien d'autres sujets à part, je ne vois pas ce que je fais différemment. N'hésitez pas à le signaler si quelque chose me manque!

La sortie, si elle aide, est fondamentalement répétée, peu importe combien je modifie le répertoire en question.

GetQueuedCompletionStatus(): Failed, Error Code 258 
+4

Pour que cela fonctionne bien n'est pas trivial (pour le moins). Si vous ne l'avez pas encore fait, je vous conseille de lire le blog de Jim Beveridge: http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html. Attention: son exemple de code est en C++, mais au moins c'est mieux que la plupart. Attention2: Je suis raisonnablement certain que, quoi que vous fassiez, ReadDirectoryChangesW ne fonctionnera jamais entièrement correctement. Je conseille généralement d'utiliser des journaux de changement à la place. http://msdn.microsoft.com/en-us/library/aa363798.aspx. Ils ne sont pas triviaux non plus, mais au moins ils fonctionnent. –

+0

@Jeffy merci, j'ai jeté un oeil à sa source, mais il n'a pas (pour autant que je peux voir) utiliser GetQueue ... ou CreateIo ... Son readme implique plus de fichiers que sont réellement là donc je pense il les a enlevés. Je commence lentement à venir à cette réalisation ... malheureusement, les revues à changement requièrent des privilèges d'administrateur que nous n'avons peut-être pas. Je pourrais toujours déclencher un service Windows pour gérer cela, je suppose. –

Répondre

6

je réalise l'affichage est généralement considéré comme horrible murs de code, mais voici comment je suis arrivé ce travail:

Nouveaux struct:

BOOL runthread; 

typedef struct overlapped_struct 
{ 
    OVERLAPPED overlapped; 
    wchar_t* buffer; 
} overlapped_t; 

typedef struct dirinfo_struct 
{ 

    HANDLE hDirOPPort; 
    HANDLE hDirFH; 
    overlapped_t* o; 
    int len_buffer; 
    wchar_t* buffer; 
    wchar_t* directory_name; 
    ULONG_PTR CompletionKey; 
} dirinfo_t; 

int somekey = 1; 

méthodes d'attribution:

void dirinfo_init(dirinfo_t* t) 
{ 
    t->buffer = malloc(16777216*sizeof(wchar_t)); 
    t->len_buffer = 16777216; 
    t->o = calloc(1, sizeof(overlapped_t)); 
    t->o->buffer = calloc(16777216, sizeof(wchar_t)); 
    memset(t->o->buffer, 0, 16777216); 
    memset(t->o, 0, sizeof(OVERLAPPED)); 
} 

void dirinfo_free(dirinfo_t* t) 
{ 
    free(t->buffer); 
    free(t->o->buffer); 
    free(t->o); 
    free(t); 
} 

Les choses importantes de main() fait ceci:

dirinfo_t* d = malloc(1*sizeof(dirinfo_t)); 
d->CompletionKey = (ULONG_PTR)&somekey; 
dirinfo_init(d); 

/* set up */ 
runthread = TRUE; 
d->hDirFH = CreateFile(L"C:\\hydratest", 
       FILE_LIST_DIRECTORY, 
       FILE_SHARE_READ|FILE_SHARE_WRITE, 
       NULL, 
       OPEN_EXISTING, 
       FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, 
       NULL); 

d->hDirOPPort = CreateIoCompletionPort(d->hDirFH, NULL, 
         (ULONG_PTR)d->CompletionKey, 1); 

Puis enfin mon fil d'attente. Voici la clé: Je ne passe pas une structure chevauchée po Je passe dans une structure contenant un OVERLAPPED plus une bonne quantité de stockage basé wchar_t. Pour des raisons que je ne comprends pas complètement, cela fonctionne. Modifier voir this answer. Je crois que la région de données agit ici comme le tampon chevauchant.

DWORD WINAPI WaitingThread(void* args) 
{ 
    DWORD errorcode = 0; // an error code 
    BOOL bResultQ = FALSE; // obvios=us 
    BOOL bResultR = FALSE; 
    DWORD NumBytes = 0; 
    FILE_NOTIFY_INFORMATION* pInfo = NULL; // the data incoming is a pointer 
              // to this struct. 
    int i = 0; 
    dirinfo_t* d = (dirinfo_t*) args;  // rescue struct from thread arg. 

Ensuite, nous obtenons sur le thread principal lui-même. Les essais et erreurs suggèrent que vous êtes supposé appeler à la fois ReadDirectoryW et GetQueueCompletionStatus. Je pense que ce que cela signifie est que nous sommes censés ne pas toucher le tampon de ReadDirectoryChangeW ** à moins que * on nous dit que nous pouvons par GetQueue. Les corrections sur cette hypothèse sont toutefois les bienvenues.

while (runthread) 
    { 
     bResultR = ReadDirectoryChangesW(d->hDirFH, (void*)d->buffer, 
              16777216, TRUE, 
       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 | FILE_NOTIFY_CHANGE_SECURITY, 
              NULL, 
              &d->o->overlapped, 
              NULL); 
     bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, 
              &NumBytes, &(d->CompletionKey), 
              (LPOVERLAPPED*)(d->o), 1000); 

Donc, maintenant que nous avons appelé ces fonctions, nous alors test qui ils ont tous deux retournés vrai. grand avertissement moche si vous avez vos paramètres mis en place à droite bResultR retourne toujours vrai, ou alors il me semble. bResultQ varie cependant selon que de nouvelles données sont sur le port.

 if (bResultQ && bResultR) 
     { 

Alors on jette ce tampon de ReadDirectoryChangesW et d'accéder aux informations de la struct.

  wprintf(L"\n"); 
      pInfo = (FILE_NOTIFY_INFORMATION*) d->buffer; 
      wprintf(L"File %s", pInfo->FileName); 
      wprintf(L" changes %d\n", pInfo->Action); 
      memset(d->buffer, 0, 16777216); 
     } 

Sinon, et thanks to Tony for this, vous pouvez ignorer en toute sécurité WAIT_TIMEOUT erreurs, mais rien d'autre signifie probablement que vous êtes en difficulté.

 else 
     { 
      errorcode = GetLastError(); 

      if (errorcode == WAIT_TIMEOUT) 
      { 
       printf("GetQueuedCompletionStatus(): Timeout\n"); 
      } 
      else 
      { 
       printf("GetQueuedCompletionStatus(): Failed\n"); 
       printf("Error Code %d\n", errorcode); 
      } 
      Sleep(500); 
     } 
    } 

    return 0; 
} 

Et cela complète ce que je pense être un exemple de travail.

Quelques notes:

  • J'ai définir la taille de la mémoire tampon pour être énorme. J'ai remarqué la copie de 100 fichiers ou alors que le tampon a manqué d'espace mis à 8192 et manqué un élément ou deux, ici et là. Donc je ne m'attends pas à ce que ça reprenne toujours tout. Ma solution serait de dire tous les 100 événements, vérifiez l'arborescence de fichiers est ce que vous pensez qu'il est si vous utilisez cette méthode. Une solution infiniment meilleure, cependant, pour constamment énumérer l'arbre potentiellement grand.
+0

hmmm intéressant. Certainement une aventure de ce truc IO. :) Merci d'avoir posté. –

+0

@Ninefingers: Salut, je travaille sur un projet similaire (mais en C++) et mon problème est de séparer le fil du dossier d'écoute de mon fil d'application, si cela ne vous dérange pas, dites-moi quand vous appelez votre WaitingThread. votre code principal mais j'ai toujours le même problème, merci d'avance. – Oumaya

+0

@Antony s'il vous plaît donner expliquer: malloc (16777216 * sizeof (wchar_t)) que je suppose 16777216 = 8192 * 2048 => Qu'est-ce que c'est 2048? Je vous remercie! – Edward83

1

Note: Pour détecter les erreurs de GetQueuedCompletionStatus correctement, car il est difficile de déterminer que cette fonction effectivement retourné, devrait être fait comme suit:

Exemple:

DWORD dwNumBytes; 
ULONG_PTR CompletionKey; 
OVERLAPPED* pOverlapped; 

//hIOCP is initialized somewhere else in the program 
BOOL bOK = GetQueuedCompletionStatus(hIOCP, &dwNumBytes, &CompletionKey, &pOverlapped, 1000); 

DWORD dwError = GetLastError(); 

if(bOK) 
{ 
// Process a successfully completed I/O event. 
} 
else 
{ 
    if (pOverlapped != NULL) 
    { 
    // Process a failed completed I/O request 
    //dwError contains the reason for failure 
    } 
    else { 
     if (dwError == WAIT_TIMEOUT) 
     { 
     //Time-out while waiting for completed I/O entry. 
     } 
     else { 
      //Bad call to GetQueuedCompletionStatus 
      //dwError contains the reason for the bad call. 
     } 
} 

Exemple tiré de le livre (Windows via C/C++) Veuillez essayer d'implémenter cette erreur de gestion dans votre code. De même, "... les threads qui appellent le GetQueuedCompletionStatus sont réveillés selon le principe du" dernier entré, premier sorti "(LIFO)."

chevauchaient Structure:

Lors de l'exécution asynchrone dispositif I/O, il faut passer l'adresse à un initialisé ENGAGES la structure via le paramètre pOverlapped. Le mot "chevauché" dans ce contexte signifie que le temps passé à exécuter des demandes d'E/S chevauche le temps que votre thread consacre à d'autres tâches.

Il parle du paramètre lorsque vous appelez ReadFile ou WriteFile, tout comme une note à ce qui précède, ce qui nécessite cette structure à initialiser.

Il se présente comme suit:

typedef struct _OVERLAPPED { 
    ULONG_PTR Internal; 
    ULONG_PTR InternalHigh; 
    union { 
    struct { 
     DWORD Offset; 
     DWORD OffsetHigh; 
    }; 
    PVOID Pointer; 
    }; 
    HANDLE hEvent; 
} OVERLAPPED, *LPOVERLAPPED; 

NOTE: Vous passez un pointeur vers une struct à votre paramètre dwCompletionKey de votre fonction CreateIoCompletionPort. Dans la référence que je regarde, ils passent simplement une constante (#define CK_FILE 1) à cela. Il dit que vous pouvez passer ce que vous voulez, car le système d'exploitation ne s'en soucie pas. Je voulais juste le signaler cependant.

+0

merci beaucoup pour cela, je sais maintenant s'attendre à un échec de contenir potentiellement des données ...! J'ai modifié ma structure Overlapped pour qu'elle soit ZeroMemory'd, j'ai pointé CompletionKey à une valeur constante, etc. Cependant, chaque fois que j'obtiens une structure avec chevauchement nul, une fausse valeur de retour et un code d'erreur timeout. –

+0

De MSDN: "Si un appel à GetQueuedCompletionStatus échoue parce que le handle de port d'achèvement associé à il est fermé alors que l'appel est en cours, la fonction retourne FALSE, * lpOverlapped sera NULL et GetLastError retournera ERROR_ABANDONED_WAIT_0. –

+0

Qu'en est-il de la valeur NumBytes, est-ce qu'elle est remplie quand elle revient? –

Questions connexes