powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / FileSystemWatcher двойной вызов события
6 сообщений из 6, страница 1 из 1
FileSystemWatcher двойной вызов события
    #36197176
mishavin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Проблема в том, что вызов события изменения файла вызывается дважды. Вот код:

watcher.Path = @"e:\";
watcher.Filter = @"txt.txt";
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.EnableRaisingEvents = true;

В коде-обработчике события приходиться вначале запрещать мониторинг файлов, а потом разрешать:

watcher.EnableRaisingEvents = false;

// здесь что-то делаю ....

watcher.EnableRaisingEvents = true;

Никто не знает в чем может быть дело?
...
Рейтинг: 0 / 0
FileSystemWatcher двойной вызов события
    #36197258
mishavinНикто не знает в чем может быть дело?Знает. Если файл записывается на самом деле дважды , то и событие возникает дважды. Например, тот же блокнот, видимо, сначала устанавливает длину файла в 0, затем записывает весь текст в него заново. Погуглите, в инете должна быть куча примеров на эту тему (в частности, пример с командной строкой типа echo some_text > 1.txt против echo some_text >> 1.txt - в последнем случае запись происходит только 1 раз, что выражается в однократном срабатывании события).
...
Рейтинг: 0 / 0
FileSystemWatcher двойной вызов события
    #36198134
mishavin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Спасибо!
...
Рейтинг: 0 / 0
FileSystemWatcher двойной вызов события
    #36199226
mishavin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Решил проверить высказывание о том, что редактор вначале обнуляет длину, а потом записывает текст. Написал Вот такую прогу:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
public delegate void delShow(string Message);

    public class ReserveObject
    {
        // событие - изменение файла
        public event delShow CurEvents;
        // объект - мониторинг изменения в файле
        private FileSystemWatcher watcher = new FileSystemWatcher();

        // конструктор
        public ReserveObject()
        {
            watcher.Path = @"e:\";
            watcher.Filter = @"txt.txt";
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
            watcher.NotifyFilter = NotifyFilters.LastWrite;
            watcher.EnableRaisingEvents = true;
        }

        private void watcher_Changed(Object sender, FileSystemEventArgs e)
        {
            FileInfo fi = new FileInfo(e.FullPath);
            CurEvents(fi.Length.ToString());            
        }
    }

    class Program
    {        
        static void Main(string[] args)
        {
            ReserveObject ro = new ReserveObject();
            ro.CurEvents += ShowStr;
            Console.ReadLine();
        }
        static void ShowStr(string st)
        {
            Console.WriteLine(st);
        }
    }

Note++ действительно сначала обнуляет, а потом пишет. А вот Bred тоже выдает два события, но с одинаковой длиной.
То есть, когда отлавливаешь изменения в файле, то корректнее ловить второе событие. Особенно это важно когда собираешься что-то делать с этим файлом делать.
Можно так же
- заблокировать событие,
- подождать, например секунду - пока изменения запишуться в файл,
- потом снова разблокировать,
- сделать какие-то действия.

Но в данном случае есть риск пропустить события-изменения файла.
...
Рейтинг: 0 / 0
FileSystemWatcher двойной вызов события
    #36199612
mishavin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кому интересно - решил привести окончательный вариант класса, отслеживающего изменения.
В нем я учел возможность вызова пары событий при изменении файла, а так же возможность одиночного вызова.
Для этого я завел словарь, куда записываю имя файла и время.
При каждом событии - проверяется словарь.
Если элемент словаря существует, то он удаляется и вызывается событие-извещение.
Если не существует - просто записывается в словарь.

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

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
    // делегат для создания события - извещения
    public delegate void delShow(string Message);

    // делегат для запуска проверки словаря в фоновом режиме
    public delegate void delCheckDict();

    //основной класс- монитор файла
    public class ReserveObject
    {
        // словарь для хранения имени файла и времени изменения
        Dictionary<string, DateTime> dic = new Dictionary<string, DateTime>();
        // событие-извещение об изменении файла
        public event delShow CurEvents;
        // объект - мониторинг изменения в файле
        private FileSystemWatcher watcher = new FileSystemWatcher();
        // для создания объекта от делегата для запуска в фоновом режиме проверки словаря
        private delCheckDict ChDict;
        // конструктор
        public ReserveObject()
        {
            watcher.Path = @"e:\";
            watcher.Filter = @"txt.txt";
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
            watcher.NotifyFilter = NotifyFilters.LastWrite;

            // запускаем проверку словаря в фоновом режиме
            ChDict = new delCheckDict(CheckDict);
            IAsyncResult ir = ChDict.BeginInvoke(null, null);

            watcher.EnableRaisingEvents = true;
        }

        private void watcher_Changed(Object sender, FileSystemEventArgs e)
        {            
            bool IsEvent = false;
            lock (this)
            {
                if (dic.ContainsKey(e.FullPath))
                {
                    dic.Remove(e.FullPath);
                    IsEvent = true;
                }
                else
                    dic.Add(e.FullPath, DateTime.Now);

            }
            if (IsEvent) MakeEvents(e.FullPath);
        }

        private void MakeEvents(string FileName)
        {
            try
            {
                FileInfo fi = new FileInfo(FileName);
                CurEvents(string.Format("Файл:{0} Длина:{1}", fi.FullName, fi.Length)); 
            }
            catch 
            {
                CurEvents("Нет файла!");
            }

        }

        private void CheckDict()
        {
            // проходим по словарю каждые 3 сек и смотрим,
            // если файл "сидит" более 3 секунд - вызываем событие с ним
            while (true)
            {
                foreach (KeyValuePair<string,DateTime> entry in dic)
                {
                    if ((entry.Value - DateTime.Now).TotalSeconds > 3000)
                    {
                        lock (this)
                        {
                            dic.Remove(entry.Key);
                        }
                        MakeEvents(entry.Key);
                    }
                }
                Thread.Sleep(3000);
            }
        }
    }

    class Program
    {        
        static void Main(string[] args)
        {
            ReserveObject ro = new ReserveObject();
            ro.CurEvents += ShowStr;
            Console.ReadLine();
        }
        static void ShowStr(string st)
        {
            Console.WriteLine(st);
        }
    }
    }
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
FileSystemWatcher двойной вызов события
    #39549696
Besm1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
mishavin,
Спасибо, очень полезный код!
Жаль только, что он не работает :) . Точнее, работает всё, кроме процедуры CheckDict . В ней есть три неточности:

1. В условии оператора if неправильно вычитается время - из раннего (время создания файла) позднее (текущее), в результате получается отрицательный результат.
2. В том же условии свойство TotalSeconds - длительность периода в секундах - сравнивается с миллисекундами.
3. Если в теле цикла
Код: c#
1.
foreach (KeyValuePair<string,DateTime> entry in dic)

попытаться удалить элемент из словаря dic , то возникает прерывание (что-то типа "нельзя осуществить навигацию по изменённой коллекции"), поскольку указатель текущего элемента при модификации/удалении/добавлении элемента коллекции разрушается.

Ниже приводится работающий вариант этой процедуры.

Всё остальное - пашет на ура. Ну, там, чуть подправить конструктор, чтобы принимал путь к папке, фильтры и т.п....

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
        private void CheckDict()
        {
            // проходим по словарю каждые 3 сек и смотрим,
            // если файл "сидит" более 3 секунд - вызываем событие с ним
            List<string> EntryKeysToRemove;

            while (true)
            {
                EntryKeysToRemove = new List<string>();
                foreach (KeyValuePair<string,DateTime> entry in dic)
                {
                    if ((DateTime.Now - entry.Value).TotalSeconds > 3) // было if ((entry.Value - DateTime.Now).TotalSeconds > 3000)
                    {
                        MakeEvents(entry.Key);
                        EntryKeysToRemove.Add(entry.Key);
                    }
                }
                foreach (string entryKey in EntryKeysToRemove) 
                {
                    lock (this)
                    {
                        dic.Remove(entryKey);
                    }
                }
                Thread.Sleep(3000);
            }
        }
...
Рейтинг: 0 / 0
6 сообщений из 6, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / FileSystemWatcher двойной вызов события
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]