gpt4 book ai didi

c++ - ReadDirectoryChangesW 和 GetOverlappedResult

转载 作者:太空宇宙 更新时间:2023-11-04 15:33:10 25 4
gpt4 key购买 nike

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

这是如何打开目录 ( basePath) 并启动“读取”线程的:

    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> )

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()它失败并产生以下错误:

Overlapped I/O event is not in a signaled state.

我错过了什么吗?是ReadDirectoryChangesW意味着被称为每个“滴答声”?或者只是在检测到新变化时?

注意:当我省略 OVERLAPPED 时struct (和 GetOverlappedResult )它可以工作,但会阻塞线程直到读取更改。这会阻止我的应用程序正确终止。 (即我无法加入线程)

最佳答案

调用GetOverlappedResult()时,如果将bWait参数设置为FALSE且I/O操作尚未完成,GetOverlappedResult() 失败,出现 ERROR_IO_INCOMPLETE 错误代码:

bWait [in]
If this parameter is TRUE, and the Internal member of the lpOverlapped structure is STATUS_PENDING, the function does not return until the operation has been completed. If this parameter is FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE.

这不是 fatal error ,因此请忽略该错误并继续。

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

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

typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

FileName
A variable-length field that contains the file name relative to the directory handle. The file name is in the Unicode character format and is not null-terminated.

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

When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded, the lpBytesReturned parameter contains zero, and the ReadDirectoryChangesW function fails with the error code ERROR_NOTIFY_ENUM_DIR.

ERROR_NOTIFY_ENUM_DIR
1022 (0x3FE)
A notify change request is being completed and the information is not being returned in the caller's buffer. The caller now needs to enumerate the files to find the changes.

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

您的线程应该看起来更像这样:

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() 或终止时向线程发出信号,这样它就可以在不忙于做某事的其余时间休眠:

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();

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);
}

关于c++ - ReadDirectoryChangesW 和 GetOverlappedResult,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43664998/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com