Terraria v1.4.4.9
Terraria source code documentation
Loading...
Searching...
No Matches
FileSystemWatcher.cs
Go to the documentation of this file.
11
12namespace System.IO;
13
15{
16 private sealed class NormalizedFilterCollection : Collection<string>
17 {
18 private sealed class ImmutableStringList : IList<string>, ICollection<string>, IEnumerable<string>, IEnumerable
19 {
20 public string[] Items = Array.Empty<string>();
21
22 public string this[int index]
23 {
24 get
25 {
26 string[] items = Items;
27 if ((uint)index >= (uint)items.Length)
28 {
29 throw new ArgumentOutOfRangeException("index");
30 }
31 return items[index];
32 }
33 set
34 {
35 string[] array = (string[])Items.Clone();
36 array[index] = value;
37 Items = array;
38 }
39 }
40
41 public int Count => Items.Length;
42
43 public bool IsReadOnly => false;
44
45 public void Add(string item)
46 {
47 throw new NotSupportedException();
48 }
49
50 public void Clear()
51 {
52 Items = Array.Empty<string>();
53 }
54
55 public bool Contains(string item)
56 {
57 return Array.IndexOf(Items, item) != -1;
58 }
59
60 public void CopyTo(string[] array, int arrayIndex)
61 {
62 Items.CopyTo(array, arrayIndex);
63 }
64
66 {
67 return ((IEnumerable<string>)Items).GetEnumerator();
68 }
69
70 public int IndexOf(string item)
71 {
72 return Array.IndexOf(Items, item);
73 }
74
75 public void Insert(int index, string item)
76 {
77 string[] items = Items;
78 string[] array = new string[items.Length + 1];
79 items.AsSpan(0, index).CopyTo(array);
80 items.AsSpan(index).CopyTo(array.AsSpan(index + 1));
81 array[index] = item;
82 Items = array;
83 }
84
85 public bool Remove(string item)
86 {
87 throw new NotSupportedException();
88 }
89
90 public void RemoveAt(int index)
91 {
92 string[] items = Items;
93 string[] array = new string[items.Length - 1];
94 items.AsSpan(0, index).CopyTo(array);
95 items.AsSpan(index + 1).CopyTo(array.AsSpan(index));
96 Items = array;
97 }
98
103 }
104
109
110 protected override void InsertItem(int index, string item)
111 {
112 base.InsertItem(index, (string.IsNullOrEmpty(item) || item == "*.*") ? "*" : item);
113 }
114
115 protected override void SetItem(int index, string item)
116 {
117 base.SetItem(index, (string.IsNullOrEmpty(item) || item == "*.*") ? "*" : item);
118 }
119
120 internal string[] GetFilters()
121 {
122 return ((ImmutableStringList)base.Items).Items;
123 }
124 }
125
126 private sealed class AsyncReadState
127 {
128 internal int Session { get; }
129
130 internal byte[] Buffer { get; }
131
133
135
137
139
148 }
149
151
152 private string _directory;
153
154 private NotifyFilters _notifyFilters = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite;
155
157
158 private bool _enabled;
159
160 private bool _initializing;
161
162 private uint _internalBufferSize = 8192u;
163
164 private bool _disposed;
165
166 private FileSystemEventHandler _onChangedHandler;
167
168 private FileSystemEventHandler _onCreatedHandler;
169
170 private FileSystemEventHandler _onDeletedHandler;
171
172 private RenamedEventHandler _onRenamedHandler;
173
174 private ErrorEventHandler _onErrorHandler;
175
176 private int _currentSession;
177
179
181 {
182 get
183 {
184 return _notifyFilters;
185 }
186 set
187 {
188 if (((uint)value & 0xFFFFFE80u) != 0)
189 {
190 throw new ArgumentException(System.SR.Format(System.SR.InvalidEnumArgument, "value", (int)value, "NotifyFilters"));
191 }
192 if (_notifyFilters != value)
193 {
195 Restart();
196 }
197 }
198 }
199
201
203 {
204 get
205 {
206 return _enabled;
207 }
208 set
209 {
210 if (_enabled != value)
211 {
212 if (IsSuspended())
213 {
214 _enabled = value;
215 }
216 else if (value)
217 {
219 }
220 else
221 {
223 }
224 }
225 }
226 }
227
228 public string Filter
229 {
230 get
231 {
232 if (Filters.Count != 0)
233 {
234 return Filters[0];
235 }
236 return "*";
237 }
238 set
239 {
240 Filters.Clear();
242 }
243 }
244
246 {
247 get
248 {
250 }
251 set
252 {
254 {
256 Restart();
257 }
258 }
259 }
260
262 {
263 get
264 {
265 return (int)_internalBufferSize;
266 }
267 set
268 {
270 {
271 if (value < 4096)
272 {
274 }
275 else
276 {
278 }
279 Restart();
280 }
281 }
282 }
283
284 [Editor("System.Diagnostics.Design.FSWPathEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
285 public string Path
286 {
287 get
288 {
289 return _directory;
290 }
291 set
292 {
293 value = ((value == null) ? string.Empty : value);
295 {
296 if (value.Length == 0)
297 {
299 }
300 if (!Directory.Exists(value))
301 {
303 }
305 Restart();
306 }
307 }
308 }
309
310 public override ISite? Site
311 {
312 get
313 {
314 return base.Site;
315 }
316 set
317 {
318 base.Site = value;
319 if (Site != null && Site.DesignMode)
320 {
321 EnableRaisingEvents = true;
322 }
323 }
324 }
325
327
328 public event FileSystemEventHandler? Changed
329 {
330 add
331 {
333 }
334 remove
335 {
337 }
338 }
339
340 public event FileSystemEventHandler? Created
341 {
342 add
343 {
345 }
346 remove
347 {
349 }
350 }
351
352 public event FileSystemEventHandler? Deleted
353 {
354 add
355 {
357 }
358 remove
359 {
361 }
362 }
363
364 public event ErrorEventHandler? Error
365 {
366 add
367 {
369 }
370 remove
371 {
373 }
374 }
375
376 public event RenamedEventHandler? Renamed
377 {
378 add
379 {
381 }
382 remove
383 {
385 }
386 }
387
389 {
390 _directory = string.Empty;
391 }
392
393 public FileSystemWatcher(string path)
394 {
397 }
398
399 public FileSystemWatcher(string path, string filter)
400 {
403 Filter = filter ?? throw new ArgumentNullException("filter");
404 }
405
406 private byte[] AllocateBuffer()
407 {
408 try
409 {
410 return new byte[_internalBufferSize];
411 }
413 {
415 }
416 }
417
418 protected override void Dispose(bool disposing)
419 {
420 try
421 {
422 if (disposing)
423 {
425 _onChangedHandler = null;
426 _onCreatedHandler = null;
427 _onDeletedHandler = null;
428 _onRenamedHandler = null;
429 _onErrorHandler = null;
430 }
431 else
432 {
434 }
435 }
436 finally
437 {
438 _disposed = true;
439 base.Dispose(disposing);
440 }
441 }
442
443 private static void CheckPathValidity(string path)
444 {
445 if (path == null)
446 {
447 throw new ArgumentNullException("path");
448 }
449 if (path.Length == 0)
450 {
452 }
453 if (!Directory.Exists(path))
454 {
456 }
457 }
458
460 {
462 if (fileName.Length == 0)
463 {
464 return false;
465 }
466 string[] filters = _filters.GetFilters();
467 if (filters.Length == 0)
468 {
469 return true;
470 }
471 string[] array = filters;
472 foreach (string text in array)
473 {
475 {
476 return true;
477 }
478 }
479 return false;
480 }
481
489
491 {
492 if (_onRenamedHandler != null && (MatchPattern(name) || MatchPattern(oldName)))
493 {
494 OnRenamed(new RenamedEventArgs(action, _directory, name.IsEmpty ? null : name.ToString(), oldName.IsEmpty ? null : oldName.ToString()));
495 }
496 }
497
498 private FileSystemEventHandler GetHandler(WatcherChangeTypes changeType)
499 {
500 return changeType switch
501 {
502 WatcherChangeTypes.Created => _onCreatedHandler,
503 WatcherChangeTypes.Deleted => _onDeletedHandler,
504 WatcherChangeTypes.Changed => _onChangedHandler,
505 _ => null,
506 };
507 }
508
510 {
511 FileSystemEventHandler handler = GetHandler(changeType);
512 if (handler != null && MatchPattern(name.IsEmpty ? ((ReadOnlySpan<char>)_directory) : name))
513 {
514 InvokeOn(new FileSystemEventArgs(changeType, _directory, name.IsEmpty ? null : name.ToString()), handler);
515 }
516 }
517
519 {
521 }
522
524 {
526 }
527
529 {
531 }
532
533 private void InvokeOn(FileSystemEventArgs e, FileSystemEventHandler handler)
534 {
535 if (handler != null)
536 {
538 if (synchronizingObject != null && synchronizingObject.InvokeRequired)
539 {
540 synchronizingObject.BeginInvoke(handler, new object[2] { this, e });
541 }
542 else
543 {
544 handler(this, e);
545 }
546 }
547 }
548
549 protected void OnError(ErrorEventArgs e)
550 {
551 ErrorEventHandler onErrorHandler = _onErrorHandler;
552 if (onErrorHandler != null)
553 {
555 if (synchronizingObject != null && synchronizingObject.InvokeRequired)
556 {
557 synchronizingObject.BeginInvoke(onErrorHandler, new object[2] { this, e });
558 }
559 else
560 {
561 onErrorHandler(this, e);
562 }
563 }
564 }
565
566 protected void OnRenamed(RenamedEventArgs e)
567 {
568 RenamedEventHandler onRenamedHandler = _onRenamedHandler;
569 if (onRenamedHandler != null)
570 {
572 if (synchronizingObject != null && synchronizingObject.InvokeRequired)
573 {
574 synchronizingObject.BeginInvoke(onRenamedHandler, new object[2] { this, e });
575 }
576 else
577 {
578 onRenamedHandler(this, e);
579 }
580 }
581 }
582
587
589 {
591 FileSystemEventHandler fileSystemEventHandler = null;
592 RenamedEventHandler renamedEventHandler = null;
593 if ((changeType & (WatcherChangeTypes.Created | WatcherChangeTypes.Deleted | WatcherChangeTypes.Changed)) != 0)
594 {
596 {
597 if ((e.ChangeType & changeType) != 0)
598 {
599 tcs.TrySetResult(new WaitForChangedResult(e.ChangeType, e.Name, null, timedOut: false));
600 }
601 };
602 if ((changeType & WatcherChangeTypes.Created) != 0)
603 {
605 }
606 if ((changeType & WatcherChangeTypes.Deleted) != 0)
607 {
609 }
610 if ((changeType & WatcherChangeTypes.Changed) != 0)
611 {
613 }
614 }
615 if ((changeType & WatcherChangeTypes.Renamed) != 0)
616 {
618 {
619 if ((e.ChangeType & changeType) != 0)
620 {
621 tcs.TrySetResult(new WaitForChangedResult(e.ChangeType, e.Name, e.OldName, timedOut: false));
622 }
623 };
625 }
626 try
627 {
630 {
631 EnableRaisingEvents = true;
632 }
633 tcs.Task.Wait(timeout);
635 }
636 finally
637 {
638 if (renamedEventHandler != null)
639 {
641 }
642 if (fileSystemEventHandler != null)
643 {
644 if ((changeType & WatcherChangeTypes.Changed) != 0)
645 {
647 }
648 if ((changeType & WatcherChangeTypes.Deleted) != 0)
649 {
651 }
652 if ((changeType & WatcherChangeTypes.Created) != 0)
653 {
655 }
656 }
657 }
658 if (!tcs.Task.IsCompletedSuccessfully)
659 {
661 }
662 return tcs.Task.Result;
663 }
664
665 private void Restart()
666 {
667 if (!IsSuspended() && _enabled)
668 {
671 }
672 }
673
675 {
676 if (_disposed)
677 {
678 throw new ObjectDisposedException(GetType().Name);
679 }
681 }
682
683 public void BeginInit()
684 {
685 bool enabled = _enabled;
687 _enabled = enabled;
688 _initializing = true;
689 }
690
691 public void EndInit()
692 {
693 _initializing = false;
694 if (_directory.Length != 0 && _enabled)
695 {
697 }
698 }
699
700 private bool IsSuspended()
701 {
702 if (!_initializing)
703 {
704 return base.DesignMode;
705 }
706 return true;
707 }
708
709 private unsafe void StartRaisingEvents()
710 {
711 if (IsSuspended())
712 {
713 _enabled = true;
714 }
715 else
716 {
718 {
719 return;
720 }
721 _directoryHandle = global::Interop.Kernel32.CreateFile(_directory, 1, FileShare.ReadWrite | FileShare.Delete, FileMode.Open, 1107296256);
723 {
724 _directoryHandle = null;
726 }
728 try
729 {
731 byte[] array = AllocateBuffer();
733 asyncReadState.PreAllocatedOverlapped = new PreAllocatedOverlapped(delegate(uint errorCode, uint numBytes, NativeOverlapped* overlappedPointer)
734 {
736 asyncReadState2.ThreadPoolBinding.FreeNativeOverlapped(overlappedPointer);
737 if (asyncReadState2.WeakWatcher.TryGetTarget(out var target))
738 {
739 target.ReadDirectoryChangesCallback(errorCode, numBytes, asyncReadState2);
740 }
742 }
743 catch
744 {
746 _directoryHandle = null;
747 throw;
748 }
749 _enabled = true;
751 }
752 }
753
754 private void StopRaisingEvents()
755 {
756 _enabled = false;
758 {
761 _directoryHandle = null;
762 }
763 }
764
765 private void FinalizeDispose()
766 {
768 {
770 }
771 }
772
773 private static bool IsHandleInvalid([NotNullWhen(false)] SafeFileHandle handle)
774 {
775 if (handle != null && !handle.IsInvalid)
776 {
777 return handle.IsClosed;
778 }
779 return true;
780 }
781
782 private unsafe void Monitor(AsyncReadState state)
783 {
784 NativeOverlapped* ptr = null;
785 bool flag = false;
786 try
787 {
788 if (_enabled && !IsHandleInvalid(state.DirectoryHandle))
789 {
790 ptr = state.ThreadPoolBinding.AllocateNativeOverlapped(state.PreAllocatedOverlapped);
791 flag = global::Interop.Kernel32.ReadDirectoryChangesW(state.DirectoryHandle, state.Buffer, _internalBufferSize, _includeSubdirectories, (uint)_notifyFilters, null, ptr, null);
792 }
793 }
795 {
796 }
798 {
799 }
800 finally
801 {
802 if (!flag)
803 {
804 if (ptr != null)
805 {
806 state.ThreadPoolBinding.FreeNativeOverlapped(ptr);
807 }
808 state.PreAllocatedOverlapped.Dispose();
809 state.ThreadPoolBinding.Dispose();
810 if (!IsHandleInvalid(state.DirectoryHandle))
811 {
813 }
814 }
815 }
816 }
817
819 {
820 try
821 {
822 if (IsHandleInvalid(state.DirectoryHandle))
823 {
824 return;
825 }
826 switch (errorCode)
827 {
828 default:
830 EnableRaisingEvents = false;
831 break;
832 case 995u:
833 break;
834 case 0u:
835 if (state.Session == Volatile.Read(ref _currentSession))
836 {
837 if (numBytes == 0)
838 {
840 }
841 else
842 {
844 }
845 }
846 break;
847 }
848 }
849 finally
850 {
851 Monitor(state);
852 }
853 }
854
856 {
858 while (sizeof(global::Interop.Kernel32.FILE_NOTIFY_INFORMATION) <= (uint)buffer.Length)
859 {
860 ref readonly global::Interop.Kernel32.FILE_NOTIFY_INFORMATION reference = ref MemoryMarshal.AsRef<global::Interop.Kernel32.FILE_NOTIFY_INFORMATION>(buffer);
861 if (reference.FileNameLength > (uint)buffer.Length - sizeof(global::Interop.Kernel32.FILE_NOTIFY_INFORMATION))
862 {
863 break;
864 }
865 ReadOnlySpan<char> readOnlySpan = MemoryMarshal.Cast<byte, char>(buffer.Slice(sizeof(global::Interop.Kernel32.FILE_NOTIFY_INFORMATION), (int)reference.FileNameLength));
866 switch (reference.Action)
867 {
868 case global::Interop.Kernel32.FileAction.FILE_ACTION_RENAMED_OLD_NAME:
870 break;
871 case global::Interop.Kernel32.FileAction.FILE_ACTION_RENAMED_NEW_NAME:
874 break;
875 default:
876 if (!oldName.IsEmpty)
877 {
880 }
881 switch (reference.Action)
882 {
883 case global::Interop.Kernel32.FileAction.FILE_ACTION_ADDED:
885 break;
886 case global::Interop.Kernel32.FileAction.FILE_ACTION_REMOVED:
888 break;
889 case global::Interop.Kernel32.FileAction.FILE_ACTION_MODIFIED:
891 break;
892 }
893 break;
894 }
895 if (reference.NextEntryOffset == 0 || reference.NextEntryOffset > (uint)buffer.Length)
896 {
897 break;
898 }
899 buffer = buffer.Slice((int)reference.NextEntryOffset);
900 }
901 if (!oldName.IsEmpty)
902 {
904 }
905 }
906}
static unsafe SafeFileHandle CreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
int IList. IndexOf(object value)
Definition Array.cs:1228
void CopyTo(KeyValuePair< TKey, TValue >[] array, int index)
static ? Delegate Remove(Delegate? source, Delegate? value)
Definition Delegate.cs:463
static ? Delegate Combine(Delegate? a, Delegate? b)
Definition Delegate.cs:379
static bool Exists([NotNullWhen(true)] string? path)
Definition Directory.cs:43
static bool MatchesSimpleExpression(ReadOnlySpan< char > expression, ReadOnlySpan< char > name, bool ignoreCase=true)
AsyncReadState(int session, byte[] buffer, SafeFileHandle handle, ThreadPoolBoundHandle binding, FileSystemWatcher parent)
WeakReference< FileSystemWatcher > WeakWatcher
FileSystemEventHandler _onCreatedHandler
void OnError(ErrorEventArgs e)
void OnCreated(FileSystemEventArgs e)
void NotifyFileSystemEventArgs(WatcherChangeTypes changeType, ReadOnlySpan< char > name)
WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout)
FileSystemEventHandler _onChangedHandler
unsafe void ParseEventBufferAndNotifyForEach(ReadOnlySpan< byte > buffer)
FileSystemEventHandler? Changed
readonly NormalizedFilterCollection _filters
unsafe void Monitor(AsyncReadState state)
FileSystemEventHandler _onDeletedHandler
void NotifyRenameEventArgs(WatcherChangeTypes action, ReadOnlySpan< char > name, ReadOnlySpan< char > oldName)
void InvokeOn(FileSystemEventArgs e, FileSystemEventHandler handler)
void OnRenamed(RenamedEventArgs e)
FileSystemEventHandler? Created
static void CheckPathValidity(string path)
WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType)
FileSystemEventHandler? Deleted
FileSystemEventHandler GetHandler(WatcherChangeTypes changeType)
void OnChanged(FileSystemEventArgs e)
void OnDeleted(FileSystemEventArgs e)
FileSystemWatcher(string path, string filter)
override void Dispose(bool disposing)
bool MatchPattern(ReadOnlySpan< char > relativePath)
RenamedEventHandler _onRenamedHandler
static bool IsHandleInvalid([NotNullWhen(false)] SafeFileHandle handle)
ISynchronizeInvoke? SynchronizingObject
void ReadDirectoryChangesCallback(uint errorCode, uint numBytes, AsyncReadState state)
static bool IsCaseSensitive
static StringComparison StringComparison
static ? string GetFileName(string? path)
Definition Path.cs:200
static string InvalidEnumArgument
Definition SR.cs:14
static string BufferSizeTooLarge
Definition SR.cs:14
static string Format(string resourceFormat, object p1)
Definition SR.cs:118
static string InvalidDirName
Definition SR.cs:20
static string FSW_BufferOverflow
Definition SR.cs:18
static string InvalidDirName_NotExists
Definition SR.cs:22
static string FSW_IOError
Definition SR.cs:16
Definition SR.cs:7
static int Increment(ref int location)
static ThreadPoolBoundHandle BindHandle(SafeHandle handle)
static unsafe? object GetNativeOverlappedState(NativeOverlapped *overlapped)
static bool Read(ref bool location)
Definition Volatile.cs:67
new IEnumerator< T > GetEnumerator()
delegate void ErrorEventHandler(object sender, ErrorEventArgs e)
delegate void RenamedEventHandler(object sender, RenamedEventArgs e)
delegate void FileSystemEventHandler(object sender, FileSystemEventArgs e)
static readonly WaitForChangedResult TimedOutResult