我对ReadDirectoryChangesW的奇怪行为感到困惑,失败的原因是错误995。下面介绍了方案。


FileHandle是使用CreateFileW获得的。
在ReadDirectoryChangesW中使用了在步骤1中获得的FileHandle。成功,并将请求发送到服务器
轮询10秒钟,如果服务器未生成任何更改通知,请使用cancelIo取消chnagenotify请求。它发送取消和服务器响应。
现在,再次使用步骤1中获得的文件句柄通过ReadDirectoryChangesW设置更改通知,它失败,并显示“ 995-由于线程退出或应用程序请求,I / O操作已中止”。
此步骤没有实际的请求发送到服务器。
立即使用在步骤1中获得的文件句柄再次调用ReadDirectoryChangesW,它会成功并将请求发送到服务器。


第3、4、5步在循环中重复,并且每个备用ReadDirectoryChangesW都失败,并显示995,而下一个立即成功。

谁能告诉我发生了什么事?下面是代码

void setnotify(WCHAR* _path)
{
    OVERLAPPED _overlapped;
    HANDLE     _handle;
    char       _buffer[8192] = {0};
    DWORD      _bufferSize = 8192;
    CnState    _state = CN_READY;
    DWORD      _inactivityTime = 0;

    typedef enum State
    {
        CN_READY,
        CN_REQUEST_PENDING,
        CN_RESPONSE_RECEIVED,
        CN_REQUEST_CANCELLED
    } CnState;

    _handle = CreateFileW(_path,
            GENERIC_READ, // access
            FILE_SHARE_READ |
            FILE_SHARE_WRITE |
            FILE_SHARE_DELETE, // share
            NULL, // sec
            OPEN_EXISTING, // disp
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags
            0);
    if (_handle == INVALID_HANDLE_VALUE)
    {
        exit(-1);
    }

    memset(&_overlapped, 0, sizeof(OVERLAPPED));

    if (!ReadDirectoryChangesW(_handle,
                _buffer,
                _bufferSize,
                true,
                0x255,
                NULL,
                &_overlapped,
                NULL)) {

        exit(-1);
    } else {
        _state = CN_REQUEST_PENDING;
        wprintf(L"Sent Change notify to Server\n");
    }


    while (1)
    {
        if ((_state == CN_REQUEST_PENDING) && (HasOverlappedIoCompleted(&_overlapped))) {
            wprintf(L"Response Received from Server\n");
            _state = CN_RESPONSE_RECEIVED;
        }

        if ((_state == CN_RESPONSE_RECEIVED) || (_state == CN_REQUEST_CANCELLED)) {
            memset(&_overlapped, 0, sizeof(OVERLAPPED));
            _inactivityTime = 0;
            if (!ReadDirectoryChangesW(_handle,
                        _buffer,
                        _bufferSize,
                        true,
                        255,
                        NULL,
                        &_overlapped,
                        NULL)) {

                wprintf(L"Sent Change notify to Server Failed.\n");
            } else {
                wprintf(L"Sent Change notify to Server\n");
                _state = CN_REQUEST_PENDING;
            }
        }

        if ((_state == ChangeNotifyRequest::CN_REQUEST_PENDING) &&
                (_inactivityTime >= 5000)){
            if (CancelIo(_handle)) {
                _state = CN_REQUEST_CANCELLED;
                wprintf(L"Cancelled Pending Requests.\n");
            } else {
                wprintf(L"Cancelled failed");
            }

        }

        Sleep(50);
        _inactivityTime += 50;

    }
}


以下是示例O / P:

已发送更改通知服务器

取消的待处理请求。

已发送更改通知服务器

取消的待处理请求。

已发送更改通知服务器失败。

已发送更改通知服务器

取消的待处理请求。

已发送更改通知服务器失败。

已发送更改通知服务器

取消的待处理请求。

已发送更改通知服务器失败。

已发送更改通知服务器

最佳答案

您开始一个操作然后将其取消,因此它的完成事件将报告一个ERROR_OPERATION_ABORTED(995)错误。但是,您在收到该事件之前就开始了一项新操作。当您调用CancelIo()时,它只是一个取消请求,原始操作仍在等待中,并且可能需要一段时间才能真正取消(或者它可能在处理取消请求之前成功完成)。因此,在开始下一个操作之前,您仍然需要等待被取消的操作实际完成,然后处理好坏的结果。

另外,您的代码中还有两个其他错误。

首次调用ReadDirectoryChangesW()时,您将dwNotifyFilter参数设置为0x255,这是错误的。您实际上仅请求以下过滤器位:

FILE_NOTIFY_CHANGE_FILE_NAME
FILE_NOTIFY_CHANGE_ATTRIBUTES
FILE_NOTIFY_CHANGE_LAST_WRITE
FILE_NOTIFY_CHANGE_CREATION


随后的调用将dwNotifFilter设置为255,这实际上是在请求以下过滤器位:

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


因此,您的过滤不一致。您真的不应该首先使用“幻数”。 Win32 API具有可用标志的#define常量,您应该按预期的方式使用它们。

最后,您没有将CreateEvent()中的Event对象与OVERLAPPED结构相关联。当您不使用I / O完成端口或I / O完成回调时,ReadDirectoryChangesW()文档中明确说明了此要求。

尝试类似这样的方法:

void setnotify(WCHAR* _path)
{
    typedef enum State
    {
        CN_READY,
        CN_REQUEST_PENDING,
        CN_REQUEST_COMPLETE
    } CnState;

    OVERLAPPED  _overlapped = {0};
    HANDLE      _handle;
    char        _buffer[8192];
    DWORD       _bufferSize;
    CnState     _state = CN_READY;
    DWORD       _inactivityTime;
    const DWORD _filter = 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;

    _handle = CreateFileW(_path,
            GENERIC_READ, // access
            FILE_SHARE_READ |
            FILE_SHARE_WRITE |
            FILE_SHARE_DELETE, // share
            NULL, // sec
            OPEN_EXISTING, // disp
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags
            0);
    if (_handle == INVALID_HANDLE_VALUE)
    {
        wprintf(L"Opening Server failed. Error: %u\n", GetLastError());
        exit(-1);
    }

    _overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (_overlapped.hEvent == NULL)
    {
        wprintf(L"Creating Overlapped Event failed. Error: %u\n", GetLastError());
        exit(-1);
    }

    do
    {
        switch (_state)
        {
            case CN_READY:
            {
                _bufferSize = 0;
                _inactivityTime = 0;

                if (!ReadDirectoryChangesW(_handle,
                        _buffer,
                        sizeof(_buffer),
                        TRUE,
                        _filter,
                        &_bufferSize,
                        &_overlapped,
                        NULL))
                {
                    wprintf(L"Requesting change notify from Server failed. Error: %u\n", GetLastError());
                    exit(-1);
                }

                _state = CN_REQUEST_PENDING;
                wprintf(L"Change notify requested from Server\n");

                break;
            }

            case CN_REQUEST_PENDING:
            {
                if (HasOverlappedIoCompleted(&_overlapped))
                {
                    _state = CN_REQUEST_COMPLETE;
                }
                else if (_inactivityTime >= 5000)
                {
                    if (CancelIo(_handle))
                    {
                        _state = CN_REQUEST_COMPLETE;
                        wprintf(L"No response in 5 seconds. Cancelling pending request\n");
                    }
                    else
                        wprintf(L"No response in 5 seconds. Cancelling pending request failed. Error: %u\n", GetLastError());
                }
                else
                {
                    Sleep(50);
                    _inactivityTime += 50;
                }

                break;
            }

            case CN_REQUEST_COMPLETE:
            {
                if (GetOverlappedResult(_handle, &_overlapped, &_bufferSize, TRUE))
                {
                    wprintf(L"Response received from Server\n");
                    // use _buffer up to _bufferSize bytes as needed...
                }
                else if (GetLastError() == ERROR_OPERATION_ABORTED)
                {
                    wprintf(L"Pending request cancelled\n");
                }
                else
                {
                    wprintf(L"Change notify from Server failed. Error: %u\n", GetLastError());
                    // handle error as needed...
                }

                _state = CN_READY:
                break;
            }
        }
    }
}


但是,如果您不打算使用I / O完成端口或I / O完成回调,则可以利用以下事实极大地简化代码:可以通过等待Event对象更有效地等待OVERLAPPED结果发出信号,而不必循环轮询OVERLAPPED状态:

void setnotify(WCHAR* _path)
{
    OVERLAPPED  _overlapped = {0};
    HANDLE      _handle;
    char        _buffer[8192];
    DWORD       _bufferSize;
    const DWORD _filter = 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;

    _handle = CreateFileW(_path,
            GENERIC_READ, // access
            FILE_SHARE_READ |
            FILE_SHARE_WRITE |
            FILE_SHARE_DELETE, // share
            NULL, // sec
            OPEN_EXISTING, // disp
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags
            0);
    if (_handle == INVALID_HANDLE_VALUE)
    {
        wprintf(L"Opening Server failed. Error: %u\n", GetLastError());
        exit(-1);
    }

    _overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (_overlapped.hEvent == NULL)
    {
        wprintf(L"Creating Overlapped Event failed. Error: %u\n", GetLastError());
        exit(-1);
    }

    do
    {
        _bufferSize = 0;

        if (!ReadDirectoryChangesW(_handle,
            _buffer,
            sizeof(_buffer),
            TRUE,
            _filter,
            &_bufferSize,
            &_overlapped,
            NULL))
        {
            wprintf(L"Requesting change notify from Server failed. Error: %u\n", GetLastError());
            exit(-1);
        }

        wprintf(L"Change notify requested from Server\n");

        // alternatively, use GetOverlappedResultEx() with a timeout
        // instead of WaitForSingleObject() and GetOverlappedResult()
        // separately...

        if (WaitForSingleObject(_overlapped.hEvent, 5000) == WAIT_TIMEOUT)
        {
            if (CancelIo(_handle))
                wprintf(L"No response in 5 seconds. Cancelling pending request\n");
            else
                wprintf(L"No response in 5 seconds. Cancelling pending request failed. Error: %u\n", GetLastError());
        }

        if (GetOverlappedResult(_handle, &_overlapped, &_bufferSize, TRUE))
        {
            wprintf(L"Response received from Server\n");
            // use _buffer up to _bufferSize bytes as needed...
        }
        else if (GetLastError() == ERROR_OPERATION_ABORTED)
        {
            wprintf(L"Pending request cancelled\n");
        }
        else
        {
            wprintf(L"Change notify from Server failed. Error: %u\n", GetLastError());
            // handle error as needed...
        }
    }
    while (true);
}


另外,请参见my earlier answera similar question,其中说明了使用ReadDirectoryChangesW()时必须了解的其他一些陷阱,尤其是对ERROR_NOTIFY_ENUM_DIR错误的处理。

10-08 08:29