How to break ReadDirectoryChangesW function in MFC?

222 Views Asked by At

In MFC, I added a button in main dialog and ReadDirectoryChangesW function in this button. If the button is clicked, the specified folder will be monitored. The problem is if I clicked button, dialog cannot be worked, because program is still running in ReadDirectoryChangesW function. I want to be able to monitor documents while using the dialog freely, is there any solution?(please refer to code)

void CGGTransferDlg::OnBnClickedAutoStartButton(CString dir)
{
    HANDLE dwRootDirChangeHandle = CreateFileA(
        dir, /* pointer to the file name */
        FILE_LIST_DIRECTORY,                /* (this is important to be FILE_LIST_DIRECTORY!) access (read-write) mode */
        FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,  /* (file share write is needed, or else user is not able to rename file while you hold it) share mode */
        NULL, /* security descriptor */
        OPEN_EXISTING, /* how to create */
        FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
        NULL /* file with attributes to copy */
        );
    if (dwRootDirChangeHandle == INVALID_HANDLE_VALUE)
    {
        printf("error: %d", GetLastError());
        return;
    }

    char notify[1024];
    memset(notify, 0, 1024);
    DWORD cbBytes;
    FILE_NOTIFY_INFORMATION *pNotify = (FILE_NOTIFY_INFORMATION *)notify;
    char str1[MAX_PATH];

    CString NewResultFileLocal, NewResultFileSever, FileExtension;
    while (1)
    {

        if (ReadDirectoryChangesW(dwRootDirChangeHandle, &notify, sizeof(notify),
            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*/, &cbBytes, NULL, NULL))//Fourth Parameter = TRUE, Get Subdirectory
        {
            int i = 0;
            memset(str1, 0, MAX_PATH);
            WideCharToMultiByte(CP_ACP, 0, pNotify->FileName, pNotify->FileNameLength / 2, str1, 99, NULL, NULL);

            NewResultFileLocal = str1;//char to cstring 
            NewResultFileLocal = dir + NewResultFileLocal;
            NewResultFileSever = str1;
            int n = NewResultFileSever.ReverseFind('\\');
            n = NewResultFileSever.GetLength() - n;
            NewResultFileSever = NewResultFileSever.Right(n - 1);

            FileExtension = PathFindExtension(NewResultFileSever);
            if (FileExtension == "aso")
                break;

            switch (pNotify->Action)
            {
            case FILE_ACTION_ADDED:
                Upload(NewResultFileLocal, NewResultFileSever);

                break;

            case FILE_ACTION_MODIFIED:
                printf("The file was modified. This can be a change in the time stamp or attributes.\n");
                break;
            case FILE_ACTION_REMOVED:
                LogSave("The file was removed from the directory: " + NewResultFileLocal);
                break;
            case FILE_ACTION_RENAMED_NEW_NAME:
                printf("The file was renamed and this is the new name.\n");
                break;
            case FILE_ACTION_RENAMED_OLD_NAME:
                printf("The file was renamed and this is the old name.\n");
                break;
            default:
                printf("Unknown command.\n");
            }

        }
    }
    ::CloseHandle(dwRootDirChangeHandle);
}
1

There are 1 best solutions below

1
Remy Lebeau On

Your UI doesn't work because you are using ReadDirectoryChangesW() in a synchronous loop within your UI thread. As such, the UI is blocked from processing other actions. Don't do that.

Move your loop to a separate worker thread, and have it notify your main UI as needed whenever a change is detected.

Also, use ReadDirectoryChangesW() asynchronously so that you can break the loop if you need to terminate the thread, such as during program exit. Create 2 event objects using CreateEvent(). Put one in an OVERLAPPED struct that you give to ReadDirectoryChangesW() so it can be signaled whenever a change occurs. Signal the other event manually when you want to terminate the thread. Then you can wait on both events using WaitForMultipleObjects(), and it will tell you whenever either event is signaled so you can act accordingly.