c++winapireaddirectorychangesw

How to continue read directory changes after 1st ReadDirectoryChangesW call?


I have implemented a program that traces changes in a given folder using ReadDirectoryChangesW in overlapped mode with a completion routine. It works good.

I am planning to reconstruct it to make a more universal library.

When the completion routine cr called, and file changes are displayed, I need to continue tracing: see line DoRDC = true; // RESTART WATCHING.

It was possible in the current version, because it run in a loop while (_kbhit() == 0) {, but after reconstruction I will eliminate this loop. WatchDirectory will call ReadDirectoryChangesW at first only.

My question is: how should I restart the tracing inside cr ? Will it be correct to call ReadDirectoryChangesW inside the completion routine cr ? But it may cause call of cr when the previous call was not finished... May be there is a way to tell ReadDirectoryChangesW not to stop tracing ?

My code is below. The main function - WatchDirectory();


LPVOID lpBuffer = NULL;
bool DoRDC = true;


VOID WINAPI cr(DWORD dwErrorCode,                
                DWORD dwNumberOfBytesTransfered,
                LPOVERLAPPED lpOverlapped) {
    // Completion routine

    DoRDC = true; // RESTART WATCHING

    std::wcout << L" ------------- lpCompletionRoutine --------------- " << std::endl;
    std::wcout << L"   ErrCode = " << dwErrorCode
        << L"  BytesTransferred = " << dwNumberOfBytesTransfered << std::endl;

    if (dwErrorCode != 0) {

        return;
    }

    std::wcout << L"..............." << std::endl;

    // simply display dir changes
    DisplayFileInfo(lpBuffer, dwNumberOfBytesTransfered);

}


bool WatchDirectory(LPTSTR lpDir)
{
    // AFTER RECONSTRUCTION WILL CALL ReadDirectoryChangesW 1 TIME WITHOUT A LOOP
    DWORD dwWaitStatus;
    HANDLE dwChangeHandles[2];
    TCHAR lpDrive[4];
    TCHAR lpFile[_MAX_FNAME];
    TCHAR lpExt[_MAX_EXT];

    HANDLE hDir = INVALID_HANDLE_VALUE;

    std::wcout << L"3_Watching for: " << lpDir << std::endl;

    _tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);

    lpDrive[2] = (TCHAR)'\\';
    lpDrive[3] = (TCHAR)'\0';

    int EventsNumber = 1;

    DWORD Flags = FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME;

    hDir = CreateFile(lpDir, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);

    if (hDir == INVALID_HANDLE_VALUE) {

        DWORD err = GetLastError();
        std::wcout << L"ERROR: CreateFile(folder to trace) function failed = " << err << std::endl;
        return false;
        ///ExitProcess(err);
    }


    DWORD nBufferLength = 30000;
    lpBuffer = malloc(nBufferLength);
    BOOL bWatchSubtree = TRUE;
    DWORD BytesReturned = 0;

    HANDLE hEvent = CreateEvent(NULL, FALSE /*manual reset = true*/, FALSE /* initial state*/, NULL);
    if (hEvent == NULL) {
        printf("\n Cannot create event.\n");
        CloseHandle(hDir);
        return false;
        //ExitProcess(GetLastError());
    }

    // =============================================================
    OVERLAPPED Overlapped;
    //---Overlapped.hEvent = hEvent;
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine = cr;

    // =============================================================

    DoRDC = true;
    while (_kbhit() == 0) {
        if (DoRDC) {
            ZeroMemory(&Overlapped, sizeof(Overlapped));
            BOOL res_rdc = ReadDirectoryChangesW(hDir,
                lpBuffer,
                nBufferLength,
                bWatchSubtree,
                Flags,
                &BytesReturned,
                &Overlapped,
                lpCompletionRoutine);

            bool ok_rdc = (res_rdc != 0);
            if (ok_rdc) {

            }
            else {
                DWORD err = GetLastError();
                std::wcout << L"ReadDirectoryChangesW error = " << err << std::endl;
            }

            DoRDC = false;
        }

        // Wait for notification.
        //std::wcout << L"SleepEx" << std::endl;

        SleepEx(1000, TRUE);
    }


    CloseHandle(hEvent);

    CloseHandle(hDir);

    free(lpBuffer);
}

Solution

  • how should I restart the tracing inside cr ? Will it be correct to call ReadDirectoryChangesW inside the completion routine cr ?

    Yes. You must call ReadDirectoryChangesW() again every time a success completion is reported.

    But it may cause call of cr when the previous call was not finished...

    The completion routine is executed when the calling thread is in an alertable state. Since you are already inside of the routine, any new requests will be cached and the routine will be called again at a later time after the routine has exited and the calling thread has a chance to enter a new alertable state.