我对ReadDirectoryChangesW失败并出现错误995的奇怪行为感到困惑。下面解释了场景。
使用CreateFileW获取FileHandle。
步骤1中获取的FileHandle用于ReadDirectoryChangesW。它成功并将请求发送到服务器
轮询10秒,如果服务器没有生成更改通知,请使用cancelIo取消chnagenotify请求。它发送取消&服务器响应。
现在再次使用在步骤1中获取的文件句柄使用ReadDirectoryChangesW设置更改通知,它失败并显示“995 - 由于线程退出或应用程序请求,I / O操作已中止”。 此步骤未向服务器发送任何实际请求。
立即使用在步骤1和步骤1中获得的文件句柄再次调用ReadDirectoryChangesW。它成功并向服务器发送请求。
步骤3,4,5在循环中重复进行。每个备用ReadDirectoryChangesW都会失败,并且995会立即成功。
谁能告诉我发生了什么事?以下是代码
{'a', 'b', 'c', 'd', 'z'}
以下是Sample O / P:
已发送更改通知到服务器
取消待处理的请求。
已发送更改通知到服务器
取消待处理的请求。
已发送更改通知到服务器失败。
已发送更改通知到服务器
取消待处理的请求。
已发送更改通知到服务器失败。
已发送更改通知到服务器
取消待处理的请求。
已发送更改通知到服务器失败。
已发送更改通知到服务器
答案 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 answer至a similar question,其中解释了使用ReadDirectoryChangesW()
时必须注意的其他一些问题,尤其是ERROR_NOTIFY_ENUM_DIR
错误的处理。