c++ - ReadDirectoryChangesW fails with error 995 if CancelIo was called before -
i'm puzzled strange behavior of readdirectorychangesw failing error 995. scenario explained below.
filehandle obtained using createfilew.
filehandle obtained in step1 used in readdirectorychangesw. success , sends request server
poll 10 seconds , if no change notify generated server, cancel chnagenotify request using cancelio. sends cancel & server responds.
now again setting change notify readdirectorychangesw using file handle obtained in step1, fails "995 - i/o operation has been aborted because of either thread exit or application request." no actual request sent server step.
immediately again call readdirectorychangesw using file handle obtained in step1 & succeeds , sends request server.
steps 3,4,5 repeated in loop & every alternate readdirectorychangesw fails 995 , immediate next 1 succeeds.
can tell me whats going on ? below 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 server\n"); } while (1) { if ((_state == cn_request_pending) && (hasoverlappediocompleted(&_overlapped))) { wprintf(l"response received 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 server failed.\n"); } else { wprintf(l"sent change notify 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 sample o/p:
sent change notify server
cancelled pending requests.
sent change notify server
cancelled pending requests.
sent change notify server failed.
sent change notify server
cancelled pending requests.
sent change notify server failed.
sent change notify server
cancelled pending requests.
sent change notify server failed.
sent change notify server
you start operation , cancel it, completion event report error_operation_aborted (995) error. but, starting new operation before have received event. when call cancelio(), request cancel, original operation still pending, , may take awhile cancel (or may complete before cancellation request processed). so, still need wait cancelled operation complete, , handle result whether or bad, before start next operation.
also, there 2 other bugs in code.
when calling readdirectorychangesw() first time, setting dwnotifyfilter parameter 0x255, wrong. requesting these filter bits:
file_notify_change_file_name file_notify_change_attributes file_notify_change_last_write file_notify_change_creation subsequent calls setting dwnotiffilter 255 instead, requesting these filter bits:
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 so, filtering inconsistent. shouldn't using "magic numbers" in first place. win32 api has #define constants available flags, should using them way intended.
lastly, not associating event object createevent() overlapped structure. requirement stated in readdirectorychangesw() documentation when not using i/o completion port or i/o completion callback.
try more instead:
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); } { 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 server failed. error: %u\n", getlasterror()); exit(-1); } _state = cn_request_pending; wprintf(l"change notify requested 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 server\n"); // use _buffer _buffersize bytes needed... } else if (getlasterror() == error_operation_aborted) { wprintf(l"pending request cancelled\n"); } else { wprintf(l"change notify server failed. error: %u\n", getlasterror()); // handle error needed... } _state = cn_ready: break; } } } } however, if not going use i/o completion port or i/o completion callback, can simplify code utilizing fact can more wait on overlapped result waiting on event object signaled, without having poll overlapped status in loop @ all:
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); } { _buffersize = 0; if (!readdirectorychangesw(_handle, _buffer, sizeof(_buffer), true, _filter, &_buffersize, &_overlapped, null)) { wprintf(l"requesting change notify server failed. error: %u\n", getlasterror()); exit(-1); } wprintf(l"change notify requested server\n"); // alternatively, use getoverlappedresultex() timeout // instead of waitforsingleobject() , 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 server\n"); // use _buffer _buffersize bytes needed... } else if (getlasterror() == error_operation_aborted) { wprintf(l"pending request cancelled\n"); } else { wprintf(l"change notify server failed. error: %u\n", getlasterror()); // handle error needed... } } while (true); } also, see my earlier answer a similar question, explains other gotchas have aware of when using readdirectorychangesw(), particularly handling of error_notify_enum_dir error.
Comments
Post a Comment