Przejdź do treści

DevStyle - Strona Główna
Problem z FileSystemWatcher.Created

Problem z FileSystemWatcher.Created

Maciej Aniserowicz

9 maja 2008

Backend

Klasa System.IO.FileSystemWatcher jest momentami wprost niezastąpiona. Nie będę opisywał tutaj jej cech, ale zajmę się jednym problemem. Zdarzenie Created daje nam znać o tym, że nowy plik pojawił się w obserwowanym katalogu. Co się jednak może stać, gdy beztrosko zaczniemy się owym plikiem zajmować? Prawdopodobne jest, że otrzymamy wyjątek. Powód takiego zachowania jest taki, że zdarzenie Created informauje nas o momencie UTWORZENIA pliku, a nie jego GOTOWOŚCI DO OBRÓBKI. W przypadku większych plików od momentu utworzenia go w katalogu do zakończenia procesu kopiowania jego zawartości może się przesypać sporo piachu w klepsydrze Piaskowego Dziadygi. Dlatego też stworzyłem klasę dziedziczącą z Watchera, która udostępnia zdarzenie AfterCreated – odpalane w momencie zakończenia tworzenia nowego pliku.
Niestety programiści tworzący FileSystemWatcher nie do końca dostosowali się do praktyk związanych z tworzeniem zdarzeń i metody OnCreated, OnRenamed itd nie są oznaczone jako wirtualne. Dlatego też podpinam się do zdarzenia Created. Po jego wystąpieniu w nowym wątku próbuję otworzyć docelowy plik – jeśli się udaje to odpalam swoje zdarzenie AfterCreated. Jeżeli nie – przechwytuję wyjątek, czekam jakiś czas i próbuję znowu. I tak w koło Macieju. Pod uwagę wzięty został równiez scenariusz usunięcia pliku w okresie pomiędzy końcem kopiowania a sprawdzeniem dostępności. W tym przypadku łapię wyjątek FileNotFoundException i kończę wątek. W celu uniknięcia problemów z wątkami i aktualizacją UI wykorzystany został mechanizm oferowany przez klasy AsyncOperation/AsyncOperationManager.
ENJOY:


 1:   public class FileSystemWatcherEx : FileSystemWatcher
2: {
3: public event EventHandler<FileSystemEventArgs> AfterCreated;
4: protected virtual void OnAfterCreated(FileSystemEventArgs e)
5: {
6: if (AfterCreated != null)
7: AfterCreated(this, e);
8: }
9:
10: private const int DEFAULT_CHECK_AVAILABILITY_INTERVAL = 500;
11: private int _checkAvailabilityInteval = DEFAULT_CHECK_AVAILABILITY_INTERVAL;
12: [DefaultValue(DEFAULT_CHECK_AVAILABILITY_INTERVAL)]
13: [Category(“Behavior”)]
14: [Description(“Determines how ofter the file is checked for availability.”)]
15: public int CheckAvailabilityInteval
16: {
17: get { return _checkAvailabilityInteval; }
18: set { _checkAvailabilityInteval = value; }
19: }
20:
21: public FileSystemWatcherEx()
22: {
23: base.Created += (sender, e) =>
24: {
25: AsyncOperation operation = AsyncOperationManager.CreateOperation(null);
26:
27: // start a new thread to watch the file
28: ThreadPool.QueueUserWorkItem(delegate
29: {
30: bool canOpen = false;
31: // loop while the file cannot be opened -> is still being used by another process
32: while (canOpen == false)
33: {
34: try
35: {
36: // try to open the file for reading – and close it immidiately if succeeded
37: File.OpenRead(e.FullPath).Close();
38: canOpen = true;
39: }
40: // can occur when a file is removed directly after processing
41: catch (FileNotFoundException)
42: {
43: break;
44: }
45: // occurs when trying to open a directory rather than a file
46: catch (UnauthorizedAccessException)
47: {
48: break;
49: }
50: catch (IOException)
51: {
52: // wait and try again
53: Thread.Sleep(CheckAvailabilityInteval);
54: }
55: }
56: if (canOpen)
57: {
58: operation.Post(delegate
59: {
60: OnAfterCreated(e);
61: }, null);
62: }
63: });
64: };
65: }
66: }

Zobacz również