0

我对 ReadDirectoryChangesW 失败并出现错误 995 的奇怪行为感到困惑。场景解释如下。

  1. FileHandle 是使用 CreateFileW 获得的。

  2. 在步骤 1 中获得的 FileHandle 用于 ReadDirectoryChangesW。它成功并将请求发送到服务器

  3. 轮询 10 秒,如果服务器没有生成更改通知,则使用 cancelIo 取消 chnagenotify 请求。它发送取消和服务器响应。

  4. 现在再次使用在步骤 1 中获得的文件句柄通过 ReadDirectoryChangesW 设置更改通知,它失败并显示“995 - 由于线程退出或应用程序请求,I/O 操作已中止”。此步骤未向服务器发送实际请求。

  5. 立即使用在步骤 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:

向服务器发送更改通知

已取消的待处理请求。

向服务器发送更改通知

已取消的待处理请求。

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

向服务器发送更改通知

已取消的待处理请求。

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

向服务器发送更改通知

已取消的待处理请求。

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

向服务器发送更改通知

4

1 回答 1

2

你开始一个操作然后取消它,所以它的完成事件将报告一个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

随后的调用将设置dwNotifFilter255改为,这实际上是在请求这些过滤器位:

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可用标志的常量,您应该按照预期的方式使用它们。

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

尝试更多类似的东西:

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 完成回调,则可以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);
}

另外,请参阅我之前类似问题的回答,其中解释了使用时必须注意的其他一些问题ReadDirectoryChangesW(),尤其是ERROR_NOTIFY_ENUM_DIR错误处理。

于 2017-08-18T17:20:12.650 回答