首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ReadDirectoryChangesW和GetOverlappedResult

ReadDirectoryChangesW和GetOverlappedResult
EN

Stack Overflow用户
提问于 2017-04-27 18:04:35
回答 1查看 2.2K关注 0票数 0

我异步调用ReadDirectoryChangesW来监视后台线程中的目录更改。

这是打开目录(basePath)和启动“读取”线程的方式:

代码语言:javascript
复制
    m_hDIR = CreateFileW(
            basePath,
            FILE_LIST_DIRECTORY | GENERIC_READ,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
            NULL);

        if (m_hDIR == INVALID_HANDLE_VALUE)
            throw CrException(CrWin32ErrorString());

        //Start reading changes in background thread
        m_Callback = std::move(a_Callback);
        m_Reading = true;
        m_ReadThread = std::thread(&CrDirectoryWatcher::StartRead, this);

我是StartRead():(注:m_Readingatomic<bool>)

代码语言:javascript
复制
void StartRead()
        {
            DWORD dwBytes = 0;
            FILE_NOTIFY_INFORMATION fni{0};
            OVERLAPPED o{0};

            //Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
            o.hEvent = CreateEvent(0, 0, 0, 0);

            while(m_Reading)
            {
                if (!ReadDirectoryChangesW(m_hDIR,
                    &fni, sizeof(fni),
                    TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
                    &dwBytes, &o, NULL))
                {
                    CrAssert(0, CrWin32ErrorString());
                }

                if (!GetOverlappedResult(m_hDIR, &o, &dwBytes, FALSE))
                    CrAssert(0, CrWin32ErrorString());

                if (fni.Action != 0)
                {
                    std::wstring fileName(fni.FileName, fni.FileNameLength);
                    m_Callback(fileName);
                    fni.Action = 0;
                }
            }
        }

基本上,我是“轮询”每一个框架的新变化。现在,当我调用GetOverlappedResult()时,它会失败并产生以下错误:

重叠I/O事件不处于信号状态。

我是不是遗漏了什么?ReadDirectoryChangesW是否意味着每一个“滴答”都被称为“滴答”?或者只是在检测到新的变化时?

注意:当我省略OVERLAPPED结构(和GetOverlappedResult)时,它可以工作,但是阻塞线程直到读取更改为止。这会阻止我的应用程序正确终止。(也就是说,我不能加入线程)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-04-27 18:16:45

当调用GetOverlappedResult()时,如果您将bWait参数设置为FALSE,而I/O操作尚未完成,则GetOverlappedResult()将在ERROR_IO_INCOMPLETE错误代码中失败:

bWait in 如果此参数为TRUE,且lpOverlapped结构的Internal成员为STATUS_PENDING,则该函数在操作完成之前不会返回。如果此参数为FALSE ,且操作仍未完成,则函数返回 FALSE GetLastError 函数返回 ERROR_IO_INCOMPLETE.

这不是致命的错误,所以只需忽略该错误并继续前进。

是的,请确保在ReadDirectoryChangesW()报告之前,不要再次调用GetOverlappedResult(),因为之前的I/O操作已经完成。

现在,尽管如此,您的代码还有另一个问题。您的线程正在堆栈上分配一个FILE_NOTIFY_INFORMATION实例。如果您查看FILE_NOTIFY_INFORMATION的定义,它的FileName字段是可变长度:

代码语言:javascript
复制
typedef struct _FILE_NOTIFY_INFORMATION {
  DWORD NextEntryOffset;
  DWORD Action;
  DWORD FileNameLength;
  WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

FileName 包含相对于目录句柄的文件名的variable-length字段。文件名为Unicode字符格式,不以空结尾.

这意味着静态分配FILE_NOTIFY_INFORMATION将太小,而且dwBytes几乎总是为0,因为ReadDirectoryChangesW()无法向您返回完整的FILE_NOTIFY_INFORMATION (除非FileName长度正好为1个字符):

当您第一次调用ReadDirectoryChangesW时,系统会分配一个缓冲区来存储更改信息。此缓冲区与目录句柄相关联,直到其关闭,且其大小在其生存期内不发生变化。在调用此函数之间发生的目录更改将添加到缓冲区,然后在下一次调用时返回。如果缓冲区溢出,则会丢弃缓冲区的全部内容,lpBytesReturned ReadDirectoryChangesW 参数包含0,而错误代码 ERROR_NOTIFY_ENUM_DIR.导致ReadDirectoryChangesW函数失败。

ERROR_NOTIFY_ENUM_DIR 1022 (0x3FE) 正在完成通知更改请求,并且调用方的缓冲区中没有返回该信息。调用方现在需要枚举文件以查找更改。

因此,您需要动态分配一个大字节缓冲区来接收FILE_NOTIFY_INFORMATION数据,然后每当GetOverlappedResult()报告该数据可用时,您就可以遍历该缓冲区。

您的线程应该更像这样:

代码语言:javascript
复制
void StartRead()
{
    DWORD dwBytes = 0;
    std::vector<BYTE> buffer(1024*64);
    OVERLAPPED o{0};
    bool bPending = false;

    //Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
    o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!o.hEvent) {
        CrAssert(0, CrWin32ErrorString());
    }

    while (m_Reading)
    {
        bPending = ReadDirectoryChangesW(m_hDIR,
            &buffer[0], buffer.size(),
            TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
            &dwBytes, &o, NULL);
        if (!bPending)
        {
            CrAssert(0, CrWin32ErrorString());
        }

        while (m_Reading)
        {
            if (GetOverlappedResult(m_hDIR, &o, &dwBytes, FALSE))
            {
                bPending = false;

                if (dwBytes != 0)
                {
                    FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[0]);
                    do
                    {
                        if (fni->Action != 0)
                        {
                            std::wstring fileName(fni->FileName, fni->FileNameLength);
                            m_Callback(fileName);
                        }

                        if (fni->NextEntryOffset == 0)
                            break;

                        fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(fni) + fni->NextEntryOffset);
                    }
                    while (true);
                }

                break;
            }

            if (GetLastError() != ERROR_IO_INCOMPLETE) {
                CrAssert(0, CrWin32ErrorString());
            }

            Sleep(10);
        }

        if (bPending)
        {
            CancelIo(m_hDIR);
            GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE);
        }
    }

    CloseHandle(o.hEvent);
}

另一种不定期轮询I/O状态的实现方法是摆脱m_Reading并使用可等待事件。让操作系统在应该调用GetOverlappedResult()或终止时向线程发出信号,这样它就可以在其余时间睡觉,而不是忙着做什么:

代码语言:javascript
复制
m_hDIR = CreateFileW(
            basePath,
            FILE_LIST_DIRECTORY | GENERIC_READ,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
            NULL);

if (m_hDIR == INVALID_HANDLE_VALUE)
    throw CrException(CrWin32ErrorString());

m_TermEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!m_TermEvent)
    throw CrException(CrWin32ErrorString());

//Start reading changes in background thread
m_Callback = std::move(a_Callback);
m_ReadThread = std::thread(&CrDirectoryWatcher::StartRead, this);

...

SetEvent(m_TermEvent);
m_ReadThread.join();

代码语言:javascript
复制
void StartRead()
{
    DWORD dwBytes = 0;
    std::vector<BYTE> buffer(1024*64);
    OVERLAPPED o{0};
    bool bPending = false, bKeepRunning = true;

    //Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
    o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!o.hEvent) {
        CrAssert(0, CrWin32ErrorString());
    }

    HANDLE h[2] = {o.hEvent, h_TermEvent};

    do
    {
        bPending = ReadDirectoryChangesW(m_hDIR,
            &buffer[0], buffer.size(),
            TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
            &dwBytes, &o, NULL);
        if (!bPending)
        {
            CrAssert(0, CrWin32ErrorString());
        }

        switch (WaitForMultipleObjects(2, h, FALSE, INFINITE))
        {
            case WAIT_OBJECT_0:
            {
                if (!GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE)) {
                    CrAssert(0, CrWin32ErrorString());
                }

                bPending = false;

                if (dwBytes == 0)
                    break;

                FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[0]);
                do
                {
                    if (fni->Action != 0)
                    {
                        std::wstring fileName(fni->FileName, fni->FileNameLength);
                        m_Callback(fileName);
                    }

                    if (fni->NextEntryOffset == 0)
                         break;

                    fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(fni) + fni->NextEntryOffset);
                }
                while (true);

                break;
            }

            case WAIT_OBJECT_0+1:
                bKeepRunning = false;
                break;

            case WAIT_FAILED:
                CrAssert(0, CrWin32ErrorString());
                break;
        }
    }
    while (bKeepRunning);

    if (bPending)
    {
        CancelIo(m_hDIR);
        GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE);
    }

    CloseHandle(o.hEvent);
}
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43664998

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档