如果之前调用了CancelIo,则ReadDirectoryChangesW失败,错误995

时间:2017-08-18 15:39:29

标签: c++ winapi readdirectorychangesw

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

  1. 使用CreateFileW获取FileHandle。

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

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

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

  5. 立即使用在步骤1和步骤1中获得的文件句柄再次调用ReadDirectoryChangesW。它成功并向服务器发送请求。

  6. 步骤3,4,5在循环中重复进行。每个备用ReadDirectoryChangesW都会失败,并且995会立即成功。

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

    {'a', 'b', 'c', 'd', 'z'}
    

    以下是Sample O / P:

    已发送更改通知到服务器

    取消待处理的请求。

    已发送更改通知到服务器

    取消待处理的请求。

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

    已发送更改通知到服务器

    取消待处理的请求。

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

    已发送更改通知到服务器

    取消待处理的请求。

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

    已发送更改通知到服务器

1 个答案:

答案 0 :(得分: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

后续调用正在将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完成回调,则可以通过利用您可以更有效地等待OVERLAPPED结果的事实来大大简化代码。等待要发信号的Event对象,而不必在循环中轮询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错误的处理。