FileSystemWatcher не сообщает об изменениях в заблокированном файле

я отслеживаю папку с помощью FileSystemWatcher, как это:

watcher = new FileSystemWatcher(folder);
watcher.NotifyFilter = NotifyFilters.Size;
watcher.Changed += changedCallback;

когда я открываю новый файл в блокноте в папке и сохранить его, я получаю уведомление. Если я продолжаю писать, а затем сохраняю, я получаю уведомление. Если я закрою файл с сохранением его, я получу уведомление. Именно этого я и хотел.

однако, оказывается, что если я создам файл в этой папке, и я установлю его режим общего доступа в FileShare.Читаю, а потом пишу ему, не получу никакого уведомления до закрытия файла. Другим обходным путем является открытие файла (например, в блокноте), что, по-видимому, приводит к обновлению его состояния, а затем мое приложение мониторинга получает уведомление. Еще одним обходным путем является обновление, которое я могу сделать в Проводнике Windows, что снова приводит к обновлению состояния файла.

интересно, что если я смотрю на Проводник Windows во время внесения изменений, я замечаю, что:

  1. если файл является общим для чтения и письма, его размер будет немедленно обновлен в Проводнике Windows, как только я сохраню его.
  2. если файл используется только для чтения, его размер не будет обновляться сразу в Проводнике Windows, если я не обновлю окно вручную.

таким образом, кажется, что мое приложение мониторинга разделяет то же поведение, что и Проводник Windows. Я думал о запуске потока, который будет просто сканировать файлы в папке, но мне интересно, есть ли что-нибудь более элегантное в этом деле.

кстати, я использую Win7, и я не уверен, что эта проблема возникает и в других версиях Windows.

спасибо!

редактировать: использование ReadDirectoryChanges в C++ дало мне те же самые точные результаты. Внедрение нить я говорил ранее не помочь. Мне интересно, что на самом деле делает F5 в Проводнике Windows, потому что это вызывает сообщение об изменении.

6 ответов


решение проблемы заключается не в том, чтобы открыть файлы, а на самом деле читать из них. Достаточно прочитать хотя бы один байт, и механизм кэша Windows запишет содержимое файла на диск, что позволит вам прочитать их.

Я закончил реализацию потока, который прошел по всем файлам, открыл их и прочитал байт из них. Это вызвало их изменение и вызвало событие в объекте FileSystemWatcher.

причина, по которой Проводник Windows F5 работает также, что Windows фактически читает содержимое файла, чтобы показать некоторое расширенное содержимое (например, миниатюры). После того, как файл читается, кэш сначала записывает на диск, вызывая событие в FSW.


да, Explorer использует тот же API, что и FileSystemWatcher. Есть только один, ReadDirectoryChangesW () как вы узнали.

то, что вы нашли, настоятельно предполагает, что Win7 оптимизирует запись на диск, которая потребуется для обновления записи каталога для файла. Откладывая его до последнего возможного момента, когда файл будет закрыт. Существует интересная корреляция между этим наблюдением и критической ошибкой, которую пользователь обнаружил в RTM-версии Win7. Этот обновление иногда не происходит вообще. Ошибка поражает случайно, но нечасто, я видел это сам только один раз на своей машине. Во всяком случае, сознательно.

детали этой теме (остерегайтесь очень медленного сервера). Это по-прежнему не удается сегодня со всеми обновлениями Win7.

Ну, интересный лакомый кусочек, но на самом деле не относится к вашему вопросу. Вам нужно изменить свой код, чтобы приспособить работу ОС.


Я столкнулся с этой же проблемой FileSystemWatcher при написании службы Windows. Служба была написана на .NET и использовала FileSystemWatcher для мониторинга файлов журнала, созданных сторонним продуктом. Во время тестирования я выполнял действия в этом стороннем продукте, который, как я знал, принудительно записывал записи журнала, но точки останова моей службы никогда не срабатывали, пока я не открыл целевой файл журнала в блокноте или не обновил свое представление в Проводнике Windows.

моим решением было создать FileInfo экземпляр (мы будем называть это fileInfoInstance) в то же время, как я создал свой FileSystemWatcher. Каждый раз, когда я запускаю или останавливаю свой FileSystemWatcher, я также запускаю или останавливаю систему.Нарезка резьбы.Таймер, обратный вызов которого вызывает fileInfoInstance.Refresh () каждые N миллисекунд. Похоже, что fileInfoInstance.Refresh () сбрасывает кэширование буферов/записи и позволяет событиям FileSystemWatcher вызывать таким же образом, как и при нажатии F5 в Проводнике.

интересно (и печально) достаточно fileInfoInstance.Справочник.Refresh () не достиг того же результата, поэтому, если вы смотрите несколько файлов, даже если они все находятся в одном каталоге и наблюдаются одним и тем же наблюдателем, вам понадобится экземпляр FileInfo для каждого файла, который вы смотрите, и ваш обратный вызов таймера должен обновить их все с каждым "тиком"...

удачи в кодировании.

Брайан


похоже, что данные файла кэшируются и фактически не записываются. Когда вы пишете что-то в файл, данные сначала помещаются в кэш с помощью определенной файловой системы IRP (запрос драйвера). Теперь, когда данные фактически записываются на диск, отправляется другой IRP. Может быть (это предположение), что FileSystemWatcher ловит только второй тип IRP.

решением (если возможно) является вызов Flush в файле, который вы пишете. Если вы отслеживаете изменения, внесенные другими приложениями, вещи может стать сложнее, конечно.


вам действительно нужен поток, который откроет все файлы и прочитает байт из них, моя программа перестала работать после того, как я запустил ее на Windows 7 вместо XP, я использовал следующий код

private void SingleByteReadThread(object notUsed)
    {
       while (true)
      {
         foreach (FileInfo fi in new DirectoryEnumerator(folderPath))
                  {
                      using (FileStream fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                          fs.ReadByte();    
                  }

          Thread.Sleep(TimeSpan.FromSeconds(2));
      }
  }

DirectoryEnumerator-мой собственный класс


для записи изменений файла необходимо вызвать метод FileStream Flush ().