Understanding DirectoryWatcher

183 Views Asked by At

I've been trying to use and understand the DirectoryWatcher class from Microsoft's Cloud Mirror sample. It uses ReadDirectoryChangesW to monitor changes to a directory. I don't think it's reporting all changes, to be honest. In any event, I had a question about the key part of the code, which is as follows:

concurrency::task<void> DirectoryWatcher::ReadChangesAsync()
{
    auto token = _cancellationTokenSource.get_token();
    return concurrency::create_task([this, token]
    {
        while (true)
        {
            DWORD returned;
            winrt::check_bool(ReadDirectoryChangesW(
                _dir.get(),
                _notify.get(),
                c_bufferSize,
                TRUE,
                FILE_NOTIFY_CHANGE_ATTRIBUTES,
                &returned,
                &_overlapped,
                nullptr));

            DWORD transferred;
            if (GetOverlappedResultEx(_dir.get(), &_overlapped, &transferred, 1000, FALSE))
            {
                std::list<std::wstring> result;
                FILE_NOTIFY_INFORMATION* next = _notify.get();
                while (next != nullptr)
                {
                    std::wstring fullPath(_path);
                    fullPath.append(L"\\");
                    fullPath.append(std::wstring_view(next->FileName, next->FileNameLength / sizeof(wchar_t)));
                    result.push_back(fullPath);

                    if (next->NextEntryOffset)
                    {
                        next = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<char*>(next) + next->NextEntryOffset);
                    }
                    else
                    {
                        next = nullptr;
                    }
                }
                _callback(result);
            }
            else if (GetLastError() != WAIT_TIMEOUT)
            {
                throw winrt::hresult_error(HRESULT_FROM_WIN32(GetLastError()));
            }
            else if (token.is_canceled())
            {
                wprintf(L"watcher cancel received\n");
                concurrency::cancel_current_task();
                return;
            }
        }
    }, token);
}

After reviewing an answer to another question, here's what I don't understand about the code above: isn't the code potentially re-calling ReadDirectoryChangesW before the prior call has returned a result? Or is this code indeed correct? Thanks for any input.

Yes, I seem to have confirmed in my testing that there should be another while loop there around the call to GetOverlappedResultEx, similar to the sample code provided in that other answer. I think the notifications are firing properly with it.

Shouldn't there also be a call to CancelIo in there, too? Or is that not necessary for some reason?

0

There are 0 best solutions below