2011-07-27 15 views
6

El siguiente es un programa mínimo que usa ReadDirectoryChangesW. El problema que estoy teniendo es que solo la primera llamada al GetQueuedCompletionStatus regresa. La segunda vez a través del ciclo bloquea para siempre sin importar cuántos cambios se realicen en el directorio.Al llamar a ReadDirectoryChangesW, solo la primera llamada devuelve los cambios (tanto de sincronización como asíncrona)

También he intentado utilizar la versión síncrona y tengo exactamente el mismo problema.

#include <array> 
#include <cassert> 
#include <iostream> 
#include <Windows.h> 

int main() { 
    // Open the directory to monitor. 
    HANDLE dir = ::CreateFileA(
     "G:\\Program Files (x86)\\Steam\\steamapps\\common\\eve online" 
    , FILE_LIST_DIRECTORY 
    , FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE 
    , NULL 
    , OPEN_EXISTING 
    , FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED 
    , NULL 
    ); 

    if (dir == INVALID_HANDLE_VALUE) { 
    std::cout << "Failed to open directory for change notifications!\n"; 
    return 1; 
    } 

    // Setup IOCP. 
    HANDLE iocp = ::CreateIoCompletionPort(
     dir 
    , NULL 
    , NULL 
    , 1 
    ); 

    // Monitor. 
    while (true) { 
    std::array<char, 1024 * 8> buf; 
    DWORD bytes_read; 
    OVERLAPPED overlapped; 
    std::memset(&overlapped, 0, sizeof(overlapped)); 
    BOOL result = ::ReadDirectoryChangesW(
     dir 
     , &buf.front() 
     , buf.size() 
     , false 
     , FILE_NOTIFY_CHANGE_FILE_NAME // Includes file creation. 
     , &bytes_read 
     , &overlapped 
     , NULL 
    ); 

    if (result == FALSE) { 
     DWORD error = ::GetLastError(); 
     std::cout << "Call to ReadDirectoryChangesW failed! " << error << "\n"; 
     return 1; 
    } 

    // Wait for completion. 
    ULONG_PTR key; 
    LPOVERLAPPED overlapped_result; 

    result = ::GetQueuedCompletionStatus(
     iocp 
     , &bytes_read 
     , &key 
     , &overlapped_result 
     , INFINITE 
    ); 

    if (result == FALSE) { 
     std::cout << "Call to GetQueuedCompletionStatus failed!\n"; 
     return 1; 
    } 

    // Print results! 
    for (FILE_NOTIFY_INFORMATION *fni = 
      reinterpret_cast<FILE_NOTIFY_INFORMATION *>(&buf.front()); 
     ; 
     fni = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(
      reinterpret_cast<char *>(fni) + fni->NextEntryOffset)) { 
     std::wstring filename(fni->FileName, fni->FileName + fni->FileNameLength); 
     std::wcout << "Got change: " << filename.c_str() << "\n"; 

     if (fni->NextEntryOffset == 0) break; 
    } 
    } 

} 

Respuesta

5

Algunos problemas.

En primer lugar, está tratando de generar literales de cadenas de varios bytes a wcout. Debería convertirlos en cadenas anchas precediendo a L.

En segundo lugar, la variable FileNameLength representa la longitud del nombre en bytes, no en caracteres. Debes dividirlo por 2 para obtener la cantidad de caracteres.

+0

Gracias, eso lo solucionó. –

0

¿Cómo está compilando esto? Con Visual Studio, no se puede compilar porque el tercer parámetro para GetQueuedCompletionStatus se escribe incorrectamente. El parámetro debe ser un puntero a un puntero a ULONG, no un puntero a ULONG. Cuando cambié la declaración de la variable "clave" a

ULONG_PTR tecla;

los programas funcionan correctamente.

+0

Estoy usando VS2010. Ese cambio no solucionó el problema. –

0

El problema es que su lógica de impresión está causando un desbordamiento del búfer porque fni-> FileNameLength está en bytes, no en caracteres. La corrupción aleatoria de la memoria explicaría por qué obtuve resultados diferentes a usted.

La solución es simplemente esto:

std :: wstring nombre de archivo (fni-> FileName fni-> nombreDeArchivo + fni-> FileNameLength/sizoeof (fni-> nombreDeArchivo [0]));

Cuestiones relacionadas