ВВЕДЕНИЕ:
Я пытаюсь использовать ReadDirectoryChangesW асинхронно в цикле.Использование ReadDirectoryChangesW асинхронно в цикле
Ниже фрагмент кода иллюстрирует то, что я пытаюсь достичь:
DWORD example()
{
DWORD error = 0;
OVERLAPPED ovl = { 0 };
ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == ovl.hEvent) return ::GetLastError();
char buffer[1024];
while(1)
{
process_list_of_existing_files();
error = ::ReadDirectoryChangesW(
m_hDirectory, // I have added FILE_FLAG_OVERLAPPED in CreateFile
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
// we have new files, append them to the list
if(error) append_new_files_to_the_list(buffer);
// just continue with the loop
else if(::GetLastError() == ERROR_IO_PENDING) continue;
// RDCW error, this is critical -> exit
else return ::GetLastError();
}
}
ПРОБЛЕМА:
Я не знаю, как обращаться со случаем, когда ReadDirectoryChangesW
возвращается FALSE
с GetLastError()
кодом быть ERROR_IO_PENDING
.
В этом случае я должен просто продолжить цикл и продолжать цикл до тех пор, пока ReadDirectoryChangesW
не вернет buffer
Я могу обработать.
МОИ ПОПЫТКИ Чтобы решить эту проблему:
Я попытался с помощью WaitForSingleObject(ovl.hEvent, 1000)
, но он выходит из строя с ошибкой 1450 ERROR_NO_SYSTEM_RESOURCES
. Ниже MVCE, который воспроизводит это поведение:
#include <iostream>
#include <Windows.h>
DWORD processDirectoryChanges(const char *buffer)
{
DWORD offset = 0;
char fileName[MAX_PATH] = "";
FILE_NOTIFY_INFORMATION *fni = NULL;
do
{
fni = (FILE_NOTIFY_INFORMATION*)(&buffer[offset]);
// since we do not use UNICODE,
// we must convert fni->FileName from UNICODE to multibyte
int ret = ::WideCharToMultiByte(CP_ACP, 0, fni->FileName,
fni->FileNameLength/sizeof(WCHAR),
fileName, sizeof(fileName), NULL, NULL);
switch (fni->Action)
{
case FILE_ACTION_ADDED:
{
std::cout << fileName << std::endl;
}
break;
default:
break;
}
::memset(fileName, '\0', sizeof(fileName));
offset += fni->NextEntryOffset;
} while (fni->NextEntryOffset != 0);
return 0;
}
int main()
{
HANDLE hDir = ::CreateFile("C:\\Users\\nenad.smiljkovic\\Desktop\\test",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hDir) return ::GetLastError();
OVERLAPPED ovl = { 0 };
ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == ovl.hEvent) return ::GetLastError();
DWORD error = 0, br;
char buffer[1024];
while (1)
{
error = ::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
if (0 == error)
{
error = ::GetLastError();
if (ERROR_IO_PENDING != error)
{
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
}
error = ::WaitForSingleObject(ovl.hEvent, 0);
switch (error)
{
case WAIT_TIMEOUT:
break;
case WAIT_OBJECT_0:
{
error = processDirectoryChanges(buffer);
if (error > 0)
{
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
if (0 == ::ResetEvent(ovl.hEvent))
{
error = ::GetLastError();
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
}
break;
default:
error = ::GetLastError();
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
break;
}
}
return 0;
}
Читая документацию, мне кажется, что мне нужно GetOverlappedResult с последним параметром, установленным в FALSE
, но я не знаю, как использовать этот API должным образом.
ВОПРОС:
Поскольку MVCE очень хорошо иллюстрирует то, что я пытаюсь сделать (напечатать имена недавно добавленных файлов), вы можете показать мне, что должно быть зафиксировано в цикле while
для того, чтобы Работа?
Опять же, необходимо использовать асинхронно ReadDirectoryChangesW
в цикле, как показано в фрагменте из ВВЕДЕНИЯ.
Этот код не имеет большого смысла. Он взрывается, потому что вы используете 0 тайм-аут вместо INFINITE, поэтому вы на самом деле не ожидаете и вызываете ReadDirectoryChangesW с очень высокой скоростью. Только когда-либо используйте OVERLAPPED, если у вас есть что-то еще. Таким образом, вы можете использовать WaitForMultipleObjects(). Как указано, вы не должны его использовать. –
@HansPassant: Если RDCW не возвращает буфер, который я могу использовать, я бы хотел продолжить цикл, выполняя другой код ('foo()' сверху), пока не получу заполненный буфер. Это был момент, когда я ждал 0 секунд. Если ничего не доступно, цикл продолжается, иначе я обрабатываю содержимое буфера. Я извиняюсь за путаницу, я не являюсь носителем английского языка, поэтому я понимаю, что мое намерение не ясно при первом чтении. – AlwaysLearningNewStuff
@AlwaysLearningNewStuff, если RDCW возвращает ошибку ожидания ввода-вывода, просто не вызывайте RDCW снова до тех пор, пока не будет сигнализировано о перекрывшемся событии. Тем временем вы можете продолжать цикл и делать другие вещи.В показанном коде вызовите RDCW один раз перед входом в цикл, а затем снова вызовите его в обработчике 'WAIT_OBJECT_0'. –