На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Обратите внимание:
1. Прежде чем начать новую тему или отправить сообщение, убедитесь, что вы не нарушаете правил форума!
2. Обязательно воспользуйтесь поиском. Возможно, Ваш вопрос уже обсуждали. Полезные ссылки приведены ниже.
3. Темы с просьбой выполнить какую-либо работу за автора в этом разделе не обсуждаются.
4. Используйте теги [ code=cpp ] ...текст программы... [ /code ] для выделения текста программы подсветкой.
5. Помните, здесь телепатов нет. Старайтесь формулировать свой вопрос максимально грамотно и чётко: Как правильно задавать вопросы
6. Запрещено отвечать в темы месячной и более давности без веских на то причин.

Полезные ссылки:
user posted image FAQ Сайта (C++) user posted image FAQ Форума user posted image Наши Исходники user posted image Поиск по Разделу user posted image MSDN Library Online (Windows Driver Kit) user posted image Google

Ваше мнение о модераторах: user posted image B.V.
Модераторы: B.V.
  
> Получение информации (событий) о созданных файлах , Получение информации (событий) о созданных файлах
    Добрый вечер!
    Далее приведена программа, которая отслеживает изменения в каталоге (например, появление нового файла).

    Отслеживание происходит нормально, но только если изменение единичное.
    Если я копирую в папку сразу 2 файла, то получаю информацию только об одном изменении - кол-во записей, которое возвращает ReadDirectoryChangesW/GetOverlappedResult = 1.

    ReadDirectoryChangesW запускается в режиме overlapped.
    Далее ожидаю сигнал о том, что передаче данных в режиме overlapped завершена.
    Запрашиваю результат overlapped через GetOverlappedResult.

    Вывожу его на экран – DisplayFileInfo(), вот здесь выводится запись только об одном изменении даже если создаю 2-3 файла сразу.
    НО если делаю переименование файла, то, как и должно быть – выводятся 2 записи – о старом и новом имени.

    Не можете ли подсказать, как получить ВСЕ изменения ?

    Исходный код:

    ExpandedWrap disabled
      #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);
      }
      Цитата
      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.
      Возможно стоит использовать для этого lpCompletionRoutine или GetQueuedCompletionStatus, а не GetOverlappedResult. При использовании ReadDirectoryChangesW (правда в случае с синхронным доступом) написано следующее
      Цитата
      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.
      Возможно что она ведет себя точно так же при асинхронном доступе и у вас в буфере просто нет места для более чем одного события. Поэтому информация аннулируется.
      Сообщение отредактировано: macomics -
        macomics, спасибо за ответ!
        Да, в msdn это читал.
        На буфер у меня отведено 10 кбайт, информация об одном файле 50-100 байт, пути и имена короткие, и при всем при этом на 2-х файлах уже затык - непохоже на переполнение.

        Не пробовал completion routine, но не совсем ясно, чем она сможет помочь. Насколько я понял из описания, она или overlapped - это не более чем способы информирования об окончании операции, а не способы наполнения буфера.
          Цитата 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.
          Значит ваш буфер ровно на одну запись
          Сообщение отредактировано: macomics -
            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 байт. Поэтому версия про недостаточность буфера очень-очень маловероятна.
              Lun2, а чему равен NextEntryOffset? По идее, именно он должен указывать на следующее изменение
                B.V., fi->NextEntryOffset == 0, т.е. 1-ая запись оказывается и последней, т.е. DisplayFileInfo все правильно выдает.
                Мой вопрос как раз и заключается в том, почему когда я создаю сразу несколько файлов в эксплорере (copy/paste несколько файлов) программа рапортует об одно изменении.
                но это не значит, что несколько записей не появляются - как я написал в исходном вопросе при переименовании появляются именно 2 записи.
                  Цитата Lun2 @
                  Отслеживание происходит нормально, но только если изменение единичное.

                  Не могу сразу решить проблему, но вот что заметил:
                  1. hDir нигде не закрывается.
                  2. Есть сомнения в алгоритме работы.
                  Вот тут:
                  ExpandedWrap disabled
                                 case WAIT_TIMEOUT:
                     
                                        // Нет сигналов
                     
                                        if (first)
                                            printf("\nNo changes in the timeout period.\n");
                                        break;

                  Если просто не обнаружено событий, попробуй перейти к участку в программе их ожидающих.
                  Т.е. если не было событий, надо снова перейти к "WaitForMultipleObjects",
                  а не к "ReadDirectoryChangesW".
                  Не знаю, поможет ли это в данном случае.
                  Алгоритм мог бы выглядеть примерно так:
                  ExpandedWrap disabled
                    // ...
                    int iWorkKey = START_WATCH;
                     
                    for(;;)
                    {
                     if(iWorkKey==START_WATCH)
                     {
                      StartRourine();
                     }
                     
                     if(iWorkKey==EXIT_WATCH) break;
                     
                     iWorkKey = WaitRoutine();
                    }
                    // ...
                  Сообщение отредактировано: ЫукпШ -
                    Цитата Lun2 @
                    ExpandedWrap disabled
                      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
                    Сообщение отредактировано: Majestio -
                      Всем спасибо!
                      В конечном счете помогло объявление "completion routine" - при ее вызове регистрируются все изменения (по крайней мере, на тестовых примерах пропусков не заметил.
                        Lun2, хорошим тоном будет публикация работающего исходника. Мы же на "исходниках", а не то, что где-то тамъ :P
                        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                        0 пользователей:


                        Рейтинг@Mail.ru
                        [ Script execution time: 0,0468 ]   [ 16 queries used ]   [ Generated: 26.04.24, 06:39 GMT ]