I'm puzzled with the strange behavior of ReadDirectoryChangesW failing with error 995. Scenario explained below.
FileHandle was obtained using CreateFileW.
FileHandle obtained in step1 was used in ReadDirectoryChangesW. It success and sends the request to server
Poll for 10 seconds and if no change notify was generated by server, cancel the chnagenotify request using cancelIo. It sends cancel & server responds.
Now again setting change notify with ReadDirectoryChangesW using the file handle obtained in step1, it fails with "995 - The I/O operation has been aborted because of either a thread exit or an application request." No actual request was sent to server by this step.
Immediately again call ReadDirectoryChangesW using the file handle obtained in step1 & it succeeds and sends request to server.
steps 3,4,5 are repeated in a loop & every alternate ReadDirectoryChangesW fails with 995 and immediate next one succeeds.
Can anyone tell me whats going on ? Below is the Code
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;
}
}
Below is the Sample O/P:
Sent Change notify to Server
Cancelled Pending Requests.
Sent Change notify to Server
Cancelled Pending Requests.
Sent Change notify to Server Failed.
Sent Change notify to Server
Cancelled Pending Requests.
Sent Change notify to Server Failed.
Sent Change notify to Server
Cancelled Pending Requests.
Sent Change notify to Server Failed.
Sent Change notify to Server
You start an operation and then cancel it, so its completion event will report back an
ERROR_OPERATION_ABORTED(995) error. But, you are starting a new operation before you have received that event. When you callCancelIo(), it is simply a request to cancel, the original operation is still pending, and may take awhile to actually cancel (or it may complete successfully before the cancellation request is processed). So, you still need to wait for the cancelled operation to actually complete, and then handle the result whether good or bad, before you then start the next operation.Also, there are two other bugs in your code.
When calling
ReadDirectoryChangesW()for the first time, you are setting thedwNotifyFilterparameter to0x255, which is wrong. You are effectively requesting only these filter bits:Subsequent calls are setting the
dwNotifFilterto255instead, which is effectively requesting these filter bits:So, your filtering is inconsistent. You really shouldn't be using "magic numbers" in the first place. The Win32 API has
#defineconstants for the available flags, you should be using them the way they are intended.Lastly, you are not associating an Event object from
CreateEvent()with theOVERLAPPEDstructure. This requirement is clearly stated in theReadDirectoryChangesW()documentation when you are not using an I/O Completion Port or I/O Completion callback.Try something more like this instead:
However, if you are not going to use an I/O Completion Port or I/O Completion callback, you can greatly simplify the code by utilizing the fact that you can more effectively wait on the
OVERLAPPEDresult by waiting on the Event object to be signaled, without having to poll theOVERLAPPEDstatus in a loop at all:Also, see my earlier answer to a similar question, which explains some other gotchas you have to be aware of when using
ReadDirectoryChangesW(), particularly handling of theERROR_NOTIFY_ENUM_DIRerror.