System.IO.FileSystemWatcher.RunningInstance.ProcessEvents C# (CSharp) Méthode

ProcessEvents() private méthode

Main processing loop. This is currently implemented as a synchronous operation that continually reads events and processes them... in the future, this could be changed to use asynchronous processing if the impact of using a thread-per-FileSystemWatcher is too high.
private ProcessEvents ( ) : void
Résultat void
            private void ProcessEvents()
            {
                // When cancellation is requested, clear out all watches.  This should force any active or future reads 
                // on the inotify handle to return 0 bytes read immediately, allowing us to wake up from the blocking call 
                // and exit the processing loop and clean up.
                var ctr = _cancellationToken.Register(obj => ((RunningInstance)obj).CancellationCallback(), this);
                try
                {
                    // Previous event information
                    string previousEventName = null;
                    WatchedDirectory previousEventParent = null;
                    uint previousEventCookie = 0;

                    // Process events as long as we're not canceled and there are more to read...
                    NotifyEvent nextEvent;
                    while (!_cancellationToken.IsCancellationRequested && TryReadEvent(out nextEvent))
                    {
                        // Try to get the actual watcher from our weak reference.  We maintain a weak reference most of the time
                        // so as to avoid a rooted cycle that would prevent our processing loop from ever ending
                        // if the watcher is dropped by the user without being disposed. If we can't get the watcher,
                        // there's nothing more to do (we can't raise events), so bail.
                        FileSystemWatcher watcher;
                        if (!_weakWatcher.TryGetTarget(out watcher))
                        {
                            break;
                        }

                        uint mask = nextEvent.mask;
                        string expandedName = null;
                        WatchedDirectory associatedDirectoryEntry = null;

                        // An overflow event means that we can't trust our state without restarting since we missed events and 
                        // some of those events could be a directory create, meaning we wouldn't have added the directory to the 
                        // watch and would not provide correct data to the caller.
                        if ((mask & (uint)Interop.Sys.NotifyEvents.IN_Q_OVERFLOW) != 0)
                        {
                            // Notify the caller of the error and, if the includeSubdirectories flag is set, restart to pick up any
                            // potential directories we missed due to the overflow.
                            watcher.NotifyInternalBufferOverflowEvent();
                            if (_includeSubdirectories)
                            {
                                watcher.Restart();
                            }
                            break;
                        }
                        else
                        {
                            // Look up the directory information for the supplied wd
                            lock (SyncObj)
                            {
                                if (!_wdToPathMap.TryGetValue(nextEvent.wd, out associatedDirectoryEntry))
                                {
                                    // The watch descriptor could be missing from our dictionary if it was removed
                                    // due to cancellation, or if we already removed it and this is a related event
                                    // like IN_IGNORED.  In any case, just ignore it... even if for some reason we 
                                    // should have the value, there's little we can do about it at this point,
                                    // and there's no more processing of this event we can do without it.
                                    continue;
                                }
                            }
                            expandedName = associatedDirectoryEntry.GetPath(true, nextEvent.name);
                        }

                        // To match Windows, ignore all changes that happen on the root folder itself
                        if (string.IsNullOrEmpty(expandedName))
                        {
                            watcher = null;
                            continue;
                        }

                        // Determine whether the affected object is a directory (rather than a file).
                        // If it is, we may need to do special processing, such as adding a watch for new 
                        // directories if IncludeSubdirectories is enabled.  Since we're only watching
                        // directories, any IN_IGNORED event is also for a directory.
                        bool isDir = (mask & (uint)(Interop.Sys.NotifyEvents.IN_ISDIR | Interop.Sys.NotifyEvents.IN_IGNORED)) != 0;

                        // Renames come in the form of two events: IN_MOVED_FROM and IN_MOVED_TO.
                        // In general, these should come as a sequence, one immediately after the other.
                        // So, we delay raising an event for IN_MOVED_FROM until we see what comes next.
                        if (previousEventName != null && ((mask & (uint)Interop.Sys.NotifyEvents.IN_MOVED_TO) == 0 || previousEventCookie != nextEvent.cookie))
                        {
                            // IN_MOVED_FROM without an immediately-following corresponding IN_MOVED_TO.
                            // We have to assume that it was moved outside of our root watch path, which
                            // should be considered a deletion to match Win32 behavior.
                            // But since we explicitly added watches on directories, if it's a directory it'll
                            // still be watched, so we need to explicitly remove the watch.
                            if (previousEventParent != null && previousEventParent.Children != null)
                            {
                                // previousEventParent will be non-null iff the IN_MOVED_FROM
                                // was for a directory, in which case previousEventParent is that directory's
                                // parent and previousEventName is the name of the directory to be removed.
                                foreach (WatchedDirectory child in previousEventParent.Children)
                                {
                                    if (child.Name == previousEventName)
                                    {
                                        RemoveWatchedDirectory(child);
                                        break;
                                    }
                                }
                            }

                            // Then fire the deletion event, even though the event was IN_MOVED_FROM.
                            watcher.NotifyFileSystemEventArgs(WatcherChangeTypes.Deleted, previousEventName);

                            previousEventName = null;
                            previousEventParent = null;
                            previousEventCookie = 0;
                        }

                        // If the event signaled that there's a new subdirectory and if we're monitoring subdirectories,
                        // add a watch for it.
                        const Interop.Sys.NotifyEvents AddMaskFilters = Interop.Sys.NotifyEvents.IN_CREATE | Interop.Sys.NotifyEvents.IN_MOVED_TO;
                        bool addWatch = ((mask & (uint)AddMaskFilters) != 0);
                        if (addWatch && isDir && _includeSubdirectories)
                        {
                            AddDirectoryWatch(associatedDirectoryEntry, nextEvent.name);
                        }

                        const Interop.Sys.NotifyEvents switchMask =
                            Interop.Sys.NotifyEvents.IN_IGNORED |Interop.Sys.NotifyEvents.IN_CREATE | Interop.Sys.NotifyEvents.IN_DELETE |
                            Interop.Sys.NotifyEvents.IN_ACCESS | Interop.Sys.NotifyEvents.IN_MODIFY | Interop.Sys.NotifyEvents.IN_ATTRIB |
                            Interop.Sys.NotifyEvents.IN_MOVED_FROM | Interop.Sys.NotifyEvents.IN_MOVED_TO;
                        switch ((Interop.Sys.NotifyEvents)(mask & (uint)switchMask))
                        {
                            case Interop.Sys.NotifyEvents.IN_CREATE:
                                watcher.NotifyFileSystemEventArgs(WatcherChangeTypes.Created, expandedName);
                                break;
                            case Interop.Sys.NotifyEvents.IN_IGNORED:
                                // We're getting an IN_IGNORED because a directory watch was removed.
                                // and we're getting this far in our code because we still have an entry for it
                                // in our dictionary.  So we want to clean up the relevant state, but not clean
                                // attempt to call back to inotify to remove the watches.
                                RemoveWatchedDirectory(associatedDirectoryEntry, removeInotify:false);
                                break;
                            case Interop.Sys.NotifyEvents.IN_DELETE:
                                watcher.NotifyFileSystemEventArgs(WatcherChangeTypes.Deleted, expandedName);
                                // We don't explicitly RemoveWatchedDirectory here, as that'll be handled
                                // by IN_IGNORED processing if this is a directory.
                                break;
                            case Interop.Sys.NotifyEvents.IN_ACCESS:
                            case Interop.Sys.NotifyEvents.IN_MODIFY:
                            case Interop.Sys.NotifyEvents.IN_ATTRIB:
                                watcher.NotifyFileSystemEventArgs(WatcherChangeTypes.Changed, expandedName);
                                break;
                            case Interop.Sys.NotifyEvents.IN_MOVED_FROM:
                                // We need to check if this MOVED_FROM event is standalone - meaning the item was moved out
                                // of scope. We do this by checking if we are at the end of our buffer (meaning no more events) 
                                // and if there is data to be read by polling the fd. If there aren't any more events, fire the
                                // deleted event; if there are more events, handle it via next pass. This adds an additional
                                // edge case where we get the MOVED_FROM event and the MOVED_TO event hasn't been generated yet
                                // so we will send a DELETE for this event and a CREATE when the MOVED_TO is eventually processed.
                                if (_bufferPos == _bufferAvailable)
                                {
                                    // Do the poll with a small timeout value.  Community research showed that a few milliseconds
                                    // was enough to allow the vast majority of MOVED_TO events that were going to show
                                    // up to actually arrive.  This doesn't need to be perfect; there's always the chance
                                    // that a MOVED_TO could show up after whatever timeout is specified, in which case
                                    // it'll just result in a delete + create instead of a rename.  We need the value to be
                                    // small so that we don't significantly delay the delivery of the deleted event in case
                                    // that's actually what's needed (otherwise it'd be fine to block indefinitely waiting
                                    // for the next event to arrive).
                                    const int MillisecondsTimeout = 2;
                                    Interop.Sys.PollEvents events;
                                    Interop.Sys.Poll(_inotifyHandle, Interop.Sys.PollEvents.POLLIN, MillisecondsTimeout, out events);

                                    // If we error or don't have any signaled handles, send the deleted event
                                    if (events == Interop.Sys.PollEvents.POLLNONE)
                                    {
                                        // There isn't any more data in the queue so this is a deleted event
                                        watcher.NotifyFileSystemEventArgs(WatcherChangeTypes.Deleted, expandedName);
                                        break;
                                    }
                                }

                                // We will set these values if the buffer has more data OR if the poll call tells us that more data is available.
                                previousEventName = expandedName;
                                previousEventParent = isDir ? associatedDirectoryEntry : null;
                                previousEventCookie = nextEvent.cookie;

                                break;
                            case Interop.Sys.NotifyEvents.IN_MOVED_TO:
                                if (previousEventName != null)
                                {
                                    // If the previous name from IN_MOVED_FROM is non-null, then this is a rename.
                                    watcher.NotifyRenameEventArgs(WatcherChangeTypes.Renamed, expandedName, previousEventName);
                                }
                                else
                                {
                                    // If it is null, then we didn't get an IN_MOVED_FROM (or we got it a long time
                                    // ago and treated it as a deletion), in which case this is considered a creation.
                                    watcher.NotifyFileSystemEventArgs(WatcherChangeTypes.Created, expandedName);
                                }
                                previousEventName = null;
                                previousEventParent = null;
                                previousEventCookie = 0;
                                break;
                        }

                        // Drop our strong reference to the watcher now that we're potentially going to block again for another read
                        watcher = null;
                    }
                }
                catch (Exception exc)
                {
                    FileSystemWatcher watcher;
                    if (_weakWatcher.TryGetTarget(out watcher))
                    {
                        watcher.OnError(new ErrorEventArgs(exc));
                    }
                }
                finally
                {
                    ctr.Dispose();
                    _inotifyHandle.Dispose();
                }
            }