pr8*_*r8x 0 c++ windows winapi multithreading
我ReadDirectoryChangesW异步调用以监视后台线程中的目录更改.
这是如何打开directory(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);
Run Code Online (Sandbox Code Playgroud)
这是StartRead():(注:m_Reading是atomic<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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,我每次都在"轮询"新的变化.现在,当我调用GetOverlappedResult()它失败并产生以下错误:
重叠的I/O事件未处于信号状态.
我错过了什么吗?是ReadDirectoryChangesW指被称为每一个"滴答"?或者只是在检测到新的变化时?
注意:当我省略OVERLAPPEDstruct(和GetOverlappedResult)时,它可以工作,但是阻塞线程直到读取更改.这可以防止我的应用程序正确终止.(即我不能加入线程)
调用时GetOverlappedResult(),如果将bWait参数设置为FALSE且I/O操作尚未完成,GetOverlappedResult()则会失败并显示ERROR_IO_INCOMPLETE错误代码:
bWait [in]
如果此参数是TRUE,并且结构的Internal成员lpOverlapped是STATUS_PENDING,则该函数在操作完成之前不会返回.如果此参数为FALSE且操作仍处于挂起状态,则函数返回FALSE并GetLastError返回函数ERROR_IO_INCOMPLETE.
这不是致命的错误,所以请忽略该错误并继续前进.
是的,请确保你不叫ReadDirectoryChangesW(),直到再次GetOverlappedResult()报告之前的I/O操作已完成第一.
现在,话虽如此,您的代码还有另一个问题.您的线程正在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;
Run Code Online (Sandbox Code Playgroud)
FileName
一个可变长度字段,包含相对于目录句柄的文件名.文件名采用Unicode字符格式,不以空值终止.
这意味着FILE_NOTIFY_INFORMATION静态分配将会太小,并且dwBytes几乎总是0,因为ReadDirectoryChangesW()无法FILE_NOTIFY_INFORMATION向您返回一个完整的(除非FileName长度恰好为1个字符):
第一次调用时
ReadDirectoryChangesW,系统会分配一个缓冲区来存储更改信息.此缓冲区与目录句柄关联,直到它关闭并且其大小在其生命周期内不会更改.在对此函数的调用之间发生的目录更改将添加到缓冲区,然后在下一次调用时返回.如果缓冲区溢出,则丢弃缓冲区的全部内容,lpBytesReturned参数包含零,并且ReadDirectoryChangesW函数将失败并显示错误代码ERROR_NOTIFY_ENUM_DIR.
ERROR_NOTIFY_ENUM_DIR
1022(0x3FE)
正在完成通知更改请求,并且未在调用方缓冲区中返回信息.调用者现在需要枚举文件以查找更改.
因此,您需要动态分配大字节缓冲区以接收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);
}
Run Code Online (Sandbox Code Playgroud)
在不定期轮询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();
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
926 次 |
| 最近记录: |