Search code examples
c++winapifilesystemsfile-monitoringreaddirectorychangesw

How should I read the filename in FILE_NOTIFY_INFORMATION struct


I'm trying to monitor file changes but I am not really sure on how to read the filename in the FILE_NOTIFY_INFORMATION struct:

    HANDLE dwChangeHandles[2];
    DWORD dwWaitStatus;
    wChangeHandles[0] = FindFirstChangeNotification(dirname.c_str(), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
    if (dwChangeHandles[0] == INVALID_HANDLE_VALUE) printerr(__FILE__,__LINE__,"FindFirstChangeNotification function failed.\n");
    ...
    if ((dwChangeHandles[0] == NULL) || (dwChangeHandles[1] == NULL))  //final validation
        printerr(__FILE__,__LINE__,"Unexpected NULL from FindFirstChangeNotification.\n");

    while (TRUE) {
        std::cout << "Waiting for notification...\n";
        dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE, INFINITE);
        if(dwWaitStatus==WAIT_OBJECT_0){
            std::cout << "Something changed\n";

            DWORD BytesReturned;
            size_t bufLen = 1024;
            FILE_NOTIFY_INFORMATION buffer[bufLen];
            if (ReadDirectoryChangesW(dwChangeHandles[0], buffer, bufLen, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &BytesReturned, NULL, NULL)){
                std::wcout << std::wstring(buffer->FileName)<< std::endl; //THERE IS NOTHING IN THE EXPECTED OUTPUT HERE
            }
            if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE ) printerr(__FILE__,__LINE__,"FindNextChangeNotification function failed.\n");
        }

        else if(dwWaitStatus==WAIT_TIMEOUT) printerr(__FILE__,__LINE__,"No changes in the timeout period.\n");
        else printerr(__FILE__,__LINE__,"Unhandled dwWaitStatus.\n");
    }

Is there something I am doing wrong


Solution

  • You have a number of problems that I can see immediately:

    1. According to the docs for the ReadDirectoryChangesW function, the buffer needs to be DWORD-aligned. As you are using a buffer on the stack this isn't guaranteed - you should allocate one from the heap instead.

    2. You don't seem to be using the function correctly. Normally you would call ReadDirectoryChangesW first, and then wait on the event. Not the other way around. When ReadDirectoryChangesW returns for an asynchronous call there is usually no data in the buffer at that point. You need to wait for notification that the request has been completed before using the buffer contents.

    3. FindNextChangeNotification is only used with FindFirstChangeNotification, so this is completely wrong. When the ReadDirectoryChangesW completes you need to use the NextEntryOffset field in the FILE_NOTIFY_INFORMATION structure to loop through the returned events.

    Edit: Since you've added more code to your question it's now obvious that you are mixing the two APIs. FindFirstChangeNotification and FindNextChangeNotification are one API, and ReadDirectoryChangesW is another. I believe you've been confused by this passage in the docs:

    This function does not indicate the change that satisfied the wait condition. To retrieve information about the specific change as part of the notification, use the ReadDirectoryChangesW function.

    I guess your confusion is understandable, but the two APIs can't be used together. If you're using FindFirstChangeNotification then all you get is a notification that something changed, and you have to re-read the directory to find out what it was. If you want specific notifications at the file level then you have to use ReadDirectoryChangesW to do the monitoring.