我对ReadDirectoryChangesW的奇怪行为感到困惑,失败的原因是错误995。下面介绍了方案。
FileHandle是使用CreateFileW获得的。
在ReadDirectoryChangesW中使用了在步骤1中获得的FileHandle。成功,并将请求发送到服务器
轮询10秒钟,如果服务器未生成任何更改通知,请使用cancelIo取消chnagenotify请求。它发送取消和服务器响应。
现在,再次使用步骤1中获得的文件句柄通过ReadDirectoryChangesW设置更改通知,它失败,并显示“ 995-由于线程退出或应用程序请求,I / O操作已中止”。
此步骤没有实际的请求发送到服务器。
立即使用在步骤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:
已发送更改通知服务器
取消的待处理请求。
已发送更改通知服务器
取消的待处理请求。
已发送更改通知服务器失败。
已发送更改通知服务器
取消的待处理请求。
已发送更改通知服务器失败。
已发送更改通知服务器
取消的待处理请求。
已发送更改通知服务器失败。
已发送更改通知服务器
最佳答案
您开始一个操作然后将其取消,因此它的完成事件将报告一个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完成回调,则可以利用以下事实极大地简化代码:可以通过等待Event对象更有效地等待
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);
}
另外,请参见my earlier answer至a similar question,其中说明了使用
ReadDirectoryChangesW()
时必须了解的其他一些陷阱,尤其是对ERROR_NOTIFY_ENUM_DIR
错误的处理。