Я озадачен странным поведением сбоя ReadDirectoryChangesW с ошибкой 995. Сценарий объяснен ниже.
FileHandle был получен с использованием CreateFileW.
FileHandle, полученный на шаге 1, использовался в ReadDirectoryChangesW. Успешно и отправляет запрос на сервер
Опрос в течение 10 секунд, и если сервер не сгенерировал уведомление об изменении, отмените запрос chnagenotify, используя cancelIo. Отправляет отмену & сервер отвечает.
Теперь снова настройку уведомлений об изменениях с помощью ReadDirectoryChangesW с использованием дескриптора файла, полученного на шаге 1, он завершается с ошибкой «995 — Операция ввода-вывода была прервана из-за выхода из потока или запроса приложения.» Этот запрос не был отправлен на сервер шаг.
Сразу же снова вызовите ReadDirectoryChangesW, используя дескриптор файла, полученный на шаге 1 & это успешно и отправляет запрос на сервер.
шаги 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
константы для доступных флагов, вы должны использовать их так, как они предназначены.
Наконец, вы не связываете объект Event из CreateEvent()
с OVERLAPPED
состав. Это требование четко указано в 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;
}
}
}
}
Однако, если вы не собираетесь использовать порт завершения ввода-вывода или обратный вызов завершения ввода-вывода, вы можете значительно упростить код, используя тот факт, что вы можете более эффективно ожидать 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);
}
Также см мой предыдущий ответ в похожий вопрос, который объясняет некоторые другие ошибки, которые вы должны знать при использовании ReadDirectoryChangesW()
в частности, обращение с ERROR_NOTIFY_ENUM_DIR
ошибка.
Других решений пока нет …