private void CleanupStreamAndWatcher() { // If we're currently streaming, stop StopStream(); // Clean up the EventStream, if it exists if (!_eventStream.IsInvalid) { _eventStream = new SafeEventStreamHandle(IntPtr.Zero); } // Make sure there's a loop to clear if (_watcherRunLoop != IntPtr.Zero) { // Stop the RunLoop and wait for the thread to exit gracefully Interop.RunLoop.CFRunLoopStop(_watcherRunLoop); _watcherThread.Join(); _watcherRunLoop = IntPtr.Zero; } // Cleanup the callback if (_callback != null) { _callback = null; } }
internal unsafe RunningInstance( FileSystemWatcher watcher, string directory, bool includeChildren, FSEventStreamEventFlags filter) { Debug.Assert(!string.IsNullOrEmpty(directory)); // Make sure _fullPath doesn't contain a link or alias since the OS will give back the actual, // non link'd or alias'd paths. _fullDirectory = System.IO.Path.GetFullPath(directory); _fullDirectory = Interop.Sys.RealPath(_fullDirectory); if (_fullDirectory is null) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, isDirectory: true); } // Also ensure it has a trailing slash. if (!_fullDirectory.EndsWith('/')) { _fullDirectory += "/"; } _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); _weakWatcher = new WeakReference <FileSystemWatcher>(watcher); _includeChildren = includeChildren; _filterFlags = filter; }
private void CreateStreamAndStartWatcher() { Debug.Assert(_eventStream.IsInvalid); Debug.Assert(_watcherRunLoop == IntPtr.Zero); Debug.Assert(_callback == null); // Make sure we only do this if there is a valid directory if (String.IsNullOrEmpty(_directory) == false) { _fullDirectory = System.IO.Path.GetFullPath(_directory); // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, 1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.libc.sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. _watcherThread = new Thread(new ThreadStart(WatchForFileSystemEventsThreadStart)); _watcherThread.Start(); _runLoopStartedEvent.WaitOne(); } }
internal void Start() { // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, 1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream if it wasn't previously created for this instance. if (_callback == null) { _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); } // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.Sys.Sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. var runLoopStarted = new ManualResetEventSlim(); new Thread(WatchForFileSystemEventsThreadStart) { IsBackground = true }.Start(runLoopStarted); runLoopStarted.Wait(); }
private void CreateStreamAndStartWatcher() { Debug.Assert(_eventStream == IntPtr.Zero); Debug.Assert(_watcherRunLoop == IntPtr.Zero); Debug.Assert(_callback == null); // Make sure we only do this if there is a valid directory if (String.IsNullOrEmpty(_directory) == false) { // Get the path to watch and verify we created the CFStringRef CFStringRef path = Interop.CoreFoundation.CFStringCreateWithCString(_directory); if (path == IntPtr.Zero) { throw Interop.GetExceptionForIoErrno(Marshal.GetLastWin32Error(), _directory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream CFArrayRef arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path }, 1); if (arrPaths == IntPtr.Zero) { throw Interop.GetExceptionForIoErrno(Marshal.GetLastWin32Error(), _directory, true); } // Create the callback for the EventStream _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.2f, EventStreamFlags); if (_eventStream == IntPtr.Zero) { throw Interop.GetExceptionForIoErrno(Marshal.GetLastWin32Error(), _directory, true); } // The paths are copied inside FSEventStreamCreate so delete our references Interop.CoreFoundation.CFRelease(path); Interop.CoreFoundation.CFRelease(arrPaths); // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. _watcherThread = new Thread(new ThreadStart(WatchForFileSystemEventsThreadStart)); _watcherThread.Start(); _runLoopStartedEvent.WaitOne(); } }
private void CleanupStreamAndWatcher() { // Make sure there's a loop to clear if (_watcherRunLoop != IntPtr.Zero) { // Stop the RunLoop and wait for the thread to exit gracefully Interop.RunLoop.CFRunLoopStop(_watcherRunLoop); _watcherThread.Join(); _watcherRunLoop = IntPtr.Zero; } // Clean up the EventStream, if it exists if (_eventStream != IntPtr.Zero) { Interop.CoreFoundation.CFRelease(_eventStream); _eventStream = IntPtr.Zero; } // Cleanup the callback if (_callback != null) { _callback = null; } }
internal unsafe void Start() { // Make sure _fullPath doesn't contain a link or alias // since the OS will give back the actual, non link'd or alias'd paths _fullDirectory = Interop.Sys.RealPath(_fullDirectory); if (_fullDirectory == null) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } Debug.Assert(string.IsNullOrEmpty(_fullDirectory) == false, "Watch directory is null or empty"); // Normalize the _fullDirectory path to have a trailing slash if (_fullDirectory[_fullDirectory.Length - 1] != '/') { _fullDirectory += "/"; } // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, (UIntPtr)1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream if it wasn't previously created for this instance. if (_callback == null) { _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); } _context = ExecutionContext.Capture(); // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.Sys.Sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } StaticWatcherRunLoopManager.ScheduleEventStream(_eventStream); bool started = Interop.EventStream.FSEventStreamStart(_eventStream); if (!started) { // Try to get the Watcher to raise the error event; if we can't do that, just silently exit since the watcher is gone anyway FileSystemWatcher watcher; if (_weakWatcher.TryGetTarget(out watcher)) { // An error occurred while trying to start the run loop so fail out watcher.OnError(new ErrorEventArgs(new IOException(SR.EventStream_FailedToStart, Marshal.GetLastWin32Error()))); } } }
internal unsafe void Start() { // Make sure _fullPath doesn't contain a link or alias // since the OS will give back the actual, non link'd or alias'd paths _fullDirectory = Interop.Sys.RealPath(_fullDirectory); if (_fullDirectory == null) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } Debug.Assert(string.IsNullOrEmpty(_fullDirectory) == false, "Watch directory is null or empty"); // Normalize the _fullDirectory path to have a trailing slash if (_fullDirectory[_fullDirectory.Length - 1] != '/') { _fullDirectory += "/"; } // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, (UIntPtr)1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream if it wasn't previously created for this instance. if (_callback == null) { _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); } // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.Sys.Sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. var runLoopStarted = new ManualResetEventSlim(); new Thread(WatchForFileSystemEventsThreadStart) { IsBackground = true }.Start(runLoopStarted); runLoopStarted.Wait(); }