FileSystemWatcher не сообщает об изменениях в заблокированном файле
Я отслеживаю папку с помощью FileSystemWatcher следующим образом:
watcher = new FileSystemWatcher(folder);
watcher.NotifyFilter = NotifyFilters.Size;
watcher.Changed += changedCallback;
Когда я открываю новый файл в блокноте в этой папке и сохраняю его, я получаю уведомление. Если я продолжу писать, а затем я сохраню, я получаю уведомление. Если я закрою файл с его сохранением, я получаю уведомление. Именно то, что я хотел.
Однако выясняется, что если я создам файл в этой папке, и я установил его режим совместного использования в FileShare.Read, а затем напишу ему, я не получу никаких уведомлений, пока файл не будет закрыт. Другим обходным решением является открытие файла (например, в "Блокноте" ), который, по-видимому, заставляет его состояние обновляться, а затем мое приложение для мониторинга получает уведомление. Еще одно обходное решение - это обновление, которое я могу сделать в проводнике Windows, что снова приводит к обновлению состояния файла.
Интересно, если я посмотрю на Проводник Windows, пока я делаю изменения, я замечаю, что:
- Если файл используется для чтения и записи, его размер будет немедленно обновлен в Проводнике Windows, как только я его сохраню.
- Если файл используется только для чтения, его размер НЕ будет обновляться немедленно в проводнике Windows, если я не обновляю вручную окно.
Итак, похоже, что мое приложение мониторинга имеет то же поведение, что и проводник Windows. Я думал о запуске потока, который будет просто сканировать файлы в папке, но мне интересно, есть ли что-нибудь более элегантное в этом случае.
Кстати, я использую Win7, и я не уверен, что эта проблема возникает и в других версиях Windows.
Спасибо!
EDIT: использование ReadDirectoryChanges в С++ принесло мне точные точные результаты. Реализация потока, о котором я говорил раньше, тоже не помогла. Мне интересно, что на самом деле делает F5 в проводнике Windows, потому что это приводит к сообщению об изменении.
Ответы
Ответ 1
Решение проблемы заключается не в том, чтобы открыть файлы, а на самом деле ПРОЧИТАТЬ от них. Достаточно прочесть хотя бы один байт, и механизм кэширования Windows напишет содержимое файла на диск, что позволит вам их прочитать.
Я закончил реализацию потока, который прошел через все файлы, открыл их и прочитал байт из них. Это вызвало их изменение и вызвало событие в объекте FileSystemWatcher.
Причина, по которой работает Windows Explorer F5, заключается в том, что Windows фактически считывает содержимое файла, чтобы показать некоторое расширенное содержимое (например, миниатюры). Как только файл читается, кеш сначала записывается на диск, вызывая событие в FSW.
Ответ 2
Да, Explorer использует тот же API, что и FileSystemWatcher. Там только один, ReadDirectoryChangesW(), как вы узнали.
То, что вы нашли, настоятельно свидетельствует о том, что Win7 оптимизирует запись на диске, которая понадобится для обновления записи в каталоге для файла. Откладывание его до последнего момента, когда файл закрыт. Там интересная корреляция между этим наблюдением и критической ошибкой, которую пользователь обнаружил в RTM-версии Win7. Обновление иногда не происходит вообще. Ошибка ударяется случайным образом, но нечасто, я видел это только один раз на своей машине. В любом случае, не зря.
Детали находятся в этот поток (остерегайтесь очень медленного сервера). Это все еще не удается сегодня, когда все обновления Win7 применяются.
Ну, интересный лакомый кусочек, но на самом деле не соответствующий вашему вопросу. Вам нужно изменить свой код для работы ОС.
Ответ 3
Я столкнулся с этой проблемой FileSystemWatcher при написании службы Windows. Служба была написана в .NET и использовала FileSystemWatcher для мониторинга файлов журналов, созданных сторонним продуктом. Во время тестирования я выполнял действия в этом стороннем продукте, который, как я знал, записывал принудительные записи в журнале, но мои контрольные точки службы никогда не запускались, пока я не открыл файл целевого журнала в блокноте или не обновил свое представление в проводнике Windows.
Моим решением было создать экземпляр FileInfo (мы будем называть это fileInfoInstance) одновременно с созданием FileSystemWatcher. Каждый раз, когда я запускаю или останавливаю свой FileSystemWatcher, я также запускаю или останавливаю System.Threading.Timer, чей обратный вызов вызывает fileInfoInstance.Refresh() каждые N миллисекунд. Похоже, что fileInfoInstance.Refresh() сбрасывает кеширование/запись кэширования и позволяет событиям FileSystemWatcher поднять так же, как и F5 в Explorer.
Интересно (и, к сожалению) достаточно fileInfoInstance.Directory.Refresh() не достигло того же результата, поэтому, если вы смотрите несколько файлов, даже если они все находятся в одном каталоге и просматриваются одним и тем же наблюдателем, вам понадобится экземпляр FileInfo для каждого просматриваемого файла, и ваш обратный вызов по таймеру должен обновить их всех с каждым тиком...
Счастливое кодирование.
Брайан
Ответ 4
Похоже, что данные файла кэшируются и на самом деле не написаны. Когда вы пишете что-то в файл, данные сначала помещаются в кеш с использованием определенной файловой системы IRP (запрос драйвера). Теперь, когда данные фактически записаны на диск, отправляется другой IRP. Это может быть (это предположение), что FileSystemWatcher ловит только второй тип IRP.
Решение (если возможно) - это вызвать Flush в файле, который вы пишете. Если вы отслеживаете изменения, внесенные другими приложениями, все может усложниться, конечно.
Ответ 5
Вам действительно нужен поток, который откроет все файлы и прочитает байт из них, моя программа перестала работать после того, как я запустил ее в 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 - это мой собственный класс
Ответ 6
Вы должны вызвать метод FileStream Flush() для записи изменений файла.