admin管理员组

文章数量:1433509

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 - the completion routine should save file changes in a queue.

Question: how can I pass a parameter to the completion routine cr, that will point to my queue for use in cr ?

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

It is possible because it runs in a loop while (_kbhit() == 0) {. After reconstruction I will eliminate this loop.

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


void DisplayFileInfo(LPVOID FileInfoRecords, DWORD FileInfoLength) {
    // Display file changes. Called from completion routine "cr".

    //ActionText[0] = L"-";

    FILE_NOTIFY_INFORMATION* fi = (FILE_NOTIFY_INFORMATION*)FileInfoRecords;

    if (FileInfoLength == 0) {
        std::wcout << L"No file info!" << std::endl;
        return;
    }

    int RecNum = 1;
    DWORD OffsetToNext = 0;

    do {

        fi = (FILE_NOTIFY_INFORMATION*)(((char*)fi) + OffsetToNext);

        std::wstring wfname;
        std::wstring wActionName;

        if ((fi->Action < MIN_ACTION_CODE) || (fi->Action > MAX_ACTION_CODE))
            wActionName = L"Unknown code";
        else
            wActionName = ActionText[fi->Action];

        int slen = fi->FileNameLength / sizeof(WCHAR);
        wfname.assign(fi->FileName, slen);

        std::wcout << L"Rec " << RecNum << L": Action = " << wActionName << L"(" << fi->Action << L")  Offset = " << fi->NextEntryOffset <<
            L"     File = " << wfname << std::endl;

        OffsetToNext = fi->NextEntryOffset;

        assert(RecNum < 50);

        RecNum++;
    } while (OffsetToNext > 0);

}

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;

    DisplayFileInfo(lpBuffer, dwNumberOfBytesTransfered);

}


bool WatchDirectory(LPTSTR lpDir)
{
    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);
}

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 - the completion routine should save file changes in a queue.

Question: how can I pass a parameter to the completion routine cr, that will point to my queue for use in cr ?

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

It is possible because it runs in a loop while (_kbhit() == 0) {. After reconstruction I will eliminate this loop.

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


void DisplayFileInfo(LPVOID FileInfoRecords, DWORD FileInfoLength) {
    // Display file changes. Called from completion routine "cr".

    //ActionText[0] = L"-";

    FILE_NOTIFY_INFORMATION* fi = (FILE_NOTIFY_INFORMATION*)FileInfoRecords;

    if (FileInfoLength == 0) {
        std::wcout << L"No file info!" << std::endl;
        return;
    }

    int RecNum = 1;
    DWORD OffsetToNext = 0;

    do {

        fi = (FILE_NOTIFY_INFORMATION*)(((char*)fi) + OffsetToNext);

        std::wstring wfname;
        std::wstring wActionName;

        if ((fi->Action < MIN_ACTION_CODE) || (fi->Action > MAX_ACTION_CODE))
            wActionName = L"Unknown code";
        else
            wActionName = ActionText[fi->Action];

        int slen = fi->FileNameLength / sizeof(WCHAR);
        wfname.assign(fi->FileName, slen);

        std::wcout << L"Rec " << RecNum << L": Action = " << wActionName << L"(" << fi->Action << L")  Offset = " << fi->NextEntryOffset <<
            L"     File = " << wfname << std::endl;

        OffsetToNext = fi->NextEntryOffset;

        assert(RecNum < 50);

        RecNum++;
    } while (OffsetToNext > 0);

}

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;

    DisplayFileInfo(lpBuffer, dwNumberOfBytesTransfered);

}


bool WatchDirectory(LPTSTR lpDir)
{
    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);
}

Share Improve this question edited Nov 18, 2024 at 19:20 Drew Dormann 64.2k14 gold badges129 silver badges197 bronze badges asked Nov 18, 2024 at 18:29 LUNLUN 1031 silver badge7 bronze badges 4
  • 1 This question currently includes multiple questions in one. It should focus on one problem only. – Drew Dormann Commented Nov 18, 2024 at 18:30
  • To be clear - a Stack Overflow question should contain one question. This site is not equipped to track multiple correct answers. – Drew Dormann Commented Nov 18, 2024 at 19:02
  • 1 OK, I have corrected my question. – LUN Commented Nov 18, 2024 at 19:16
  • Better use CreateThreadpoolIo instead spin in loop – RbMm Commented Nov 19, 2024 at 1:24
Add a comment  | 

1 Answer 1

Reset to default 2

Define a custom struct that contains an OVERLAPPED as its 1st data member, and any additional members you need, such as (a pointer to) your queue.

Then, pass a pointer to that struct in the LPOVERLAPPED parameter of ReadDirectoryChangesW(). cr() can then cast the LPOVERLAPPED pointer back to your struct to access its members. This works because the 1st data member of a struct is guaranteed to have the same memory address as the struct itself.

This is a common approach to passing around custom data in overlapped I/O operations.

For example:

struct MY_OVERLAPPED : OVERLAPPED
{
    // extra members as needed...
};

VOID WINAPI cr(DWORD dwErrorCode,                
                DWORD dwNumberOfBytesTransfered,
                LPOVERLAPPED lpOverlapped) {
    ...

    MY_OVERLAPPED *lpMyOverlapped = static_cast<MY_OVERLAPPED*>(lpOverlapped);
    // use lpMyOverlapped members as needed...

    ...
}


bool WatchDirectory(LPTSTR lpDir)
{
    ...

    MY_OVERLAPPED Overlapped;
    // populate Overlapped members as needed...

    ...

    BOOL res_rdc = ReadDirectoryChangesW(..., &Overlapped, cr);

    ...
}

本文标签: cSome questions about getting last files changes via ReadDirectoryChangesWStack Overflow