Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.137.220.120] |
|
Сообщ.
#1
,
|
|
|
Добрый вечер!
Далее приведена программа, которая отслеживает изменения в каталоге (например, появление нового файла). Отслеживание происходит нормально, но только если изменение единичное. Если я копирую в папку сразу 2 файла, то получаю информацию только об одном изменении - кол-во записей, которое возвращает ReadDirectoryChangesW/GetOverlappedResult = 1. ReadDirectoryChangesW запускается в режиме overlapped. Далее ожидаю сигнал о том, что передаче данных в режиме overlapped завершена. Запрашиваю результат overlapped через GetOverlappedResult. Вывожу его на экран – DisplayFileInfo(), вот здесь выводится запись только об одном изменении даже если создаю 2-3 файла сразу. НО если делаю переименование файла, то, как и должно быть – выводятся 2 записи – о старом и новом имени. Не можете ли подсказать, как получить ВСЕ изменения ? Исходный код: #include <iostream> #include <windows.h> #include <stdlib.h> #include <stdio.h> #include <tchar.h> #include <assert.h> // https://docs.microsoft.com/ru-ru/windows/win32/fileio/obtaining-directory-change-notifications // Получение уведомлений об изменении каталога и файлов в нём void RefreshDirectory(LPTSTR, HANDLE, DWORD); void RefreshTree(LPTSTR, HANDLE, DWORD); void WatchDirectory(LPTSTR); int _tmain(int argc, TCHAR* argv[]) { setlocale(LC_ALL, "Russian"); if (argc != 2) { _tprintf(TEXT("Usage: %s <dir>\n"), argv[0]); return 0; } WatchDirectory(argv[1]); } /*#define FILE_ACTION_ADDED 0x00000001 #define FILE_ACTION_REMOVED 0x00000002 #define FILE_ACTION_MODIFIED 0x00000003 #define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 #define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 */ // справочник наименований действий с файлами: const WCHAR * ActionText[] = { L"-", L"file added", L"file removed", L"file modified", L"file ranamed (old)", L"file renamed (new)" }; #define MIN_ACTION_CODE 1 #define MAX_ACTION_CODE 5 void DisplayFileInfo(LPVOID FileInfoRecords, DWORD FileInfoLength) { //ActionText[0] = L"-"; FILE_NOTIFY_INFORMATION* fi = (FILE_NOTIFY_INFORMATION*)FileInfoRecords; if (FileInfoLength == 0) { std::wcout << L"No file info!" << std::endl; return; } int RecNum = 1; DWORD OffsetToNext = 0; do { // следующая запись fi = (FILE_NOTIFY_INFORMATION*)(((char*)fi) + OffsetToNext); std::wstring wfname; std::wstring wActionName; if ((fi->Action < MIN_ACTION_CODE) || (fi->Action > MAX_ACTION_CODE)) wActionName = L"Unknown code"; else wActionName = ActionText[fi->Action]; int slen = fi->FileNameLength / sizeof(WCHAR); wfname.assign(fi->FileName, slen); // Выдаем информацию об изменениях std::wcout << L"Rec " << RecNum << L": Action = " << wActionName << L"(" << fi->Action << L") Offset = " << fi->NextEntryOffset << L" File = " << wfname << std::endl; OffsetToNext = fi->NextEntryOffset; assert(RecNum < 50); RecNum++; } while (OffsetToNext > 0); } void WatchDirectory(LPTSTR lpDir) { DWORD dwWaitStatus; HANDLE dwChangeHandles[2]; TCHAR lpDrive[4]; TCHAR lpFile[_MAX_FNAME]; TCHAR lpExt[_MAX_EXT]; std::wcout << L"Watchng for: " << lpDir << std::endl; // Разбивает имя пути на компоненты _tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT); lpDrive[2] = (TCHAR)'\\'; lpDrive[3] = (TCHAR)'\0'; int EventsNumber = 1; // флаги для ReadDirectoryChangesW DWORD Flags = FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME; // открываем каталог, за которым следим HANDLE hDir = CreateFile(lpDir, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (hDir == INVALID_HANDLE_VALUE) { // ошибка открытия каталога отслеживания DWORD err = GetLastError(); std::wcout << L"ERROR: CreateFile(folder to trace) function failed = " << err << std::endl; ExitProcess(err); } // --- инициализируем структуры данных и параметры для ReadDirectoryChangesW --- DWORD nBufferLength = 10000; LPVOID lpBuffer = malloc(nBufferLength); BOOL bWatchSubtree = TRUE; DWORD BytesReturned = 0; // --- создаем событие для Overlapped --- HANDLE hEvent = CreateEvent(NULL, FALSE /*manual reset = true*/, FALSE /* initial state*/, NULL); if (hEvent == NULL) { printf("\n Cannot create event.\n"); ExitProcess(GetLastError()); } bool first = true; while (TRUE) { // Wait for notification. // ============================================================= OVERLAPPED Overlapped; Overlapped.hEvent = hEvent; LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine = NULL; // запрашиваем изменения в режие overlapped BOOL res_rdc = ReadDirectoryChangesW(hDir, lpBuffer, nBufferLength, bWatchSubtree, Flags, &BytesReturned, &Overlapped, lpCompletionRoutine); bool ok_rdc = (res_rdc != 0); if (ok_rdc) { if (first) printf("\nWaiting for notification...\n"); // начинаем ожидание завершения overlapped-функции dwChangeHandles[0] = Overlapped.hEvent; dwWaitStatus = WaitForMultipleObjects(EventsNumber, dwChangeHandles, FALSE, 3000); switch (dwWaitStatus) { case WAIT_OBJECT_0: { printf("\n WAIT_OBJECT_0 .\n"); DWORD NumberOfBytesTransferred = 0; BOOL ok_gor = GetOverlappedResult(hDir, &Overlapped, &NumberOfBytesTransferred, FALSE); if (ok_gor == 0) { // ошибка DWORD err = GetLastError(); if (err == ERROR_IO_INCOMPLETE) std::wcout << L"Err (GetOverlappedResult) = ERROR_IO_INCOMPLETE" << std::endl; else std::wcout << L"Err (GetOverlappedResult) = " << err << std::endl; } else { // нет ошибки: overplapped-функция (ReadDirectoryChangesW) завершилась нормально std::wcout << L"GetOverlappedResult = OK. Bytes = " << NumberOfBytesTransferred << std::endl; // отображаем изменения, полученные от ReadDirectoryChangesW DisplayFileInfo(lpBuffer, NumberOfBytesTransferred /*FileInfoLength*/); } break; } case WAIT_TIMEOUT: // Нет сигналов if (first) printf("\nNo changes in the timeout period.\n"); break; default: printf("\n ERROR: Unhandled dwWaitStatus.\n"); ExitProcess(GetLastError()); break; } } else { // ошибка DWORD err = GetLastError(); std::wcout << L"ReadDirectoryChangesW error = " << err << std::endl; } // ============================================================= first = false; } free(lpBuffer); } |
Сообщ.
#2
,
|
|
|
Цитата Возможно стоит использовать для этого lpCompletionRoutine или GetQueuedCompletionStatus, а не GetOverlappedResult. При использовании ReadDirectoryChangesW (правда в случае с синхронным доступом) написано следующееFor asynchronous completion, you can receive notification in one of three ways: Using the GetOverlappedResult function. To receive notification through GetOverlappedResult, do not specify a completion routine in the lpCompletionRoutine parameter. Be sure to set the hEvent member of the OVERLAPPED structure to a unique event. Using the GetQueuedCompletionStatus function. To receive notification through GetQueuedCompletionStatus, do not specify a completion routine in lpCompletionRoutine. Associate the directory handle hDirectory with a completion port by calling the CreateIoCompletionPort function. Using a completion routine. To receive notification through a completion routine, do not associate the directory with a completion port. Specify a completion routine in lpCompletionRoutine. This routine is called whenever the operation has been completed or canceled while the thread is in an alertable wait state. The hEvent member of the OVERLAPPED structure is not used by the system, so you can use it yourself. Цитата Возможно что она ведет себя точно так же при асинхронном доступе и у вас в буфере просто нет места для более чем одного события. Поэтому информация аннулируется. When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, ReadDirectoryChangesW will still return true, but the entire contents of the buffer are discarded and the lpBytesReturned parameter will be zero, which indicates that your buffer was too small to hold all of the changes that occurred. |
Сообщ.
#3
,
|
|
|
macomics, спасибо за ответ!
Да, в msdn это читал. На буфер у меня отведено 10 кбайт, информация об одном файле 50-100 байт, пути и имена короткие, и при всем при этом на 2-х файлах уже затык - непохоже на переполнение. Не пробовал completion routine, но не совсем ясно, чем она сможет помочь. Насколько я понял из описания, она или overlapped - это не более чем способы информирования об окончании операции, а не способы наполнения буфера. |
Сообщ.
#4
,
|
|
|
Цитата Lun2 @ На буфер у меня отведено 10 кбайт, информация об одном файле 50-100 байт, пути и имена короткие, и при всем при этом на 2-х файлах уже затык - непохоже на переполнение. Я просто не нашел где вы инициализируете lpBuffer->NextEntryOffset Цитата Значит ваш буфер ровно на одну запись NextEntryOffset The number of bytes that must be skipped to get to the next record. A value of zero indicates that this is the last record. |
Сообщ.
#5
,
|
|
|
macomics,
Цитата macomics @ Цитата Lun2 @ На буфер у меня отведено 10 кбайт, информация об одном файле 50-100 байт, пути и имена короткие, и при всем при этом на 2-х файлах уже затык - непохоже на переполнение. Я просто не нашел где вы инициализируете lpBuffer->NextEntryOffset Цитата Значит ваш буфер ровно на одну записьNextEntryOffset The number of bytes that must be skipped to get to the next record. A value of zero indicates that this is the last record. Размер буфер определяется здесь: DWORD nBufferLength = 10000; LPVOID lpBuffer = malloc(nBufferLength); lpBuffer наполняется вызовом ReadDirectoryChangesW, он же инициализирует все поля буфера, в т.ч. и NextEntryOffset. Этот коммент я не понял - "Значит ваш буфер ровно на одну запись", кол-во записей д.б. "сколько влезет" в буфер.Исходя из того, что DisplayFileInfo выдает одну запись, это не я завел буфер под одну запись, а он записал одну... Про достаточность буфера я делаю исходя из того, что на 1 запись приходится очень мало байт (50-100), а я захватил буфер на 10000 байт. Поэтому версия про недостаточность буфера очень-очень маловероятна. |
Сообщ.
#6
,
|
|
|
Lun2, а чему равен NextEntryOffset? По идее, именно он должен указывать на следующее изменение
|
Сообщ.
#7
,
|
|
|
B.V., fi->NextEntryOffset == 0, т.е. 1-ая запись оказывается и последней, т.е. DisplayFileInfo все правильно выдает.
Мой вопрос как раз и заключается в том, почему когда я создаю сразу несколько файлов в эксплорере (copy/paste несколько файлов) программа рапортует об одно изменении. но это не значит, что несколько записей не появляются - как я написал в исходном вопросе при переименовании появляются именно 2 записи. |
Сообщ.
#8
,
|
|
|
Цитата Lun2 @ Отслеживание происходит нормально, но только если изменение единичное. Не могу сразу решить проблему, но вот что заметил: 1. hDir нигде не закрывается. 2. Есть сомнения в алгоритме работы. Вот тут: case WAIT_TIMEOUT: // Нет сигналов if (first) printf("\nNo changes in the timeout period.\n"); break; Если просто не обнаружено событий, попробуй перейти к участку в программе их ожидающих. Т.е. если не было событий, надо снова перейти к "WaitForMultipleObjects", а не к "ReadDirectoryChangesW". Не знаю, поможет ли это в данном случае. Алгоритм мог бы выглядеть примерно так: // ... int iWorkKey = START_WATCH; for(;;) { if(iWorkKey==START_WATCH) { StartRourine(); } if(iWorkKey==EXIT_WATCH) break; iWorkKey = WaitRoutine(); } // ... |
Сообщ.
#9
,
|
|
|
Цитата Lun2 @ OVERLAPPED Overlapped; Посмотри выполнение этого условия: Цитата Any unused members of this structure should always be initialized to zero before the structure is used in a function call. Otherwise, the function may fail and return ERROR_INVALID_PARAMETER. https://learn.microsoft.com/en-us/windows/w...base-overlapped |
Сообщ.
#10
,
|
|
|
Всем спасибо!
В конечном счете помогло объявление "completion routine" - при ее вызове регистрируются все изменения (по крайней мере, на тестовых примерах пропусков не заметил. |
Сообщ.
#11
,
|
|
|
Lun2, хорошим тоном будет публикация работающего исходника. Мы же на "исходниках", а не то, что где-то тамъ
|