FileSystemWatcher.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Services / IO / System / IO / FileSystemWatcher.cs / 1305376 / FileSystemWatcher.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.IO { 
    using System.Text; 
    using System.Runtime.Serialization.Formatters;
    using System.Threading; 
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System; 
    using System.Collections;
    using System.IO; 
    using System.Reflection; 
    using System.ComponentModel;
    using System.ComponentModel.Design; 
    using Microsoft.Win32;
    using Microsoft.Win32.SafeHandles;
    using System.Security.Permissions;
    using System.Security; 
    using System.Globalization;
    using System.Runtime.Versioning; 
 
    /// 
    ///    Listens to the system directory change notifications and 
    ///       raises events when a directory or file within a directory changes.
    /// 
    [
    DefaultEvent("Changed"), 
    // Disabling partial trust scenarios
    PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), 
    PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust"), 
    IODescription(SR.FileSystemWatcherDesc)
    ] 
    public class FileSystemWatcher : Component, ISupportInitialize {
        /// 
        ///     Private instance variables
        ///  
        // Directory being monitored
        private string directory; 
 
        // Filter for name matching
        private string filter; 

        // Unmanaged handle to monitored directory
        private SafeFileHandle directoryHandle;
 
        // The watch filter for the API call.
        private const NotifyFilters defaultNotifyFilters = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; 
        private NotifyFilters notifyFilters = defaultNotifyFilters; 

        // Flag to watch subtree of this directory 
        private bool includeSubdirectories = false;

        // Flag to note whether we are attached to the thread pool and responding to changes
        private bool enabled = false; 

        // Are we in init? 
        private bool initializing = false; 

        // Buffer size 
        private int internalBufferSize = 8192;

        // Used for synchronization
        private WaitForChangedResult changedResult; 
        private bool isChanged = false;
        private ISynchronizeInvoke synchronizingObject; 
        private bool readGranted; 
        private bool disposed;
        // Current "session" ID to ignore old events whenever we stop then 
        // restart.
        private int currentSession;

        // Event handlers 
        private FileSystemEventHandler onChangedHandler = null;
        private FileSystemEventHandler onCreatedHandler = null; 
        private FileSystemEventHandler onDeletedHandler = null; 
        private RenamedEventHandler onRenamedHandler = null;
        private ErrorEventHandler onErrorHandler = null; 

        // Thread gate holder and constats
        private bool stopListening = false;
 
        // Used for async method
        private bool runOnce = false; 
 
        // To validate the input for "path"
        private static readonly char[] wildcards = new char[] { '?', '*' }; 

        private static int notifyFiltersValidMask;

        // Additional state information to pass to callback.  Note that we 
        // never return this object to users, but we do pass state in it.
        private sealed class FSWAsyncResult : IAsyncResult 
        { 
            internal int session;
            internal byte[] buffer; 
            public bool IsCompleted {
                get { throw new NotImplementedException(); }
            }
 
            public WaitHandle AsyncWaitHandle {
                get { throw new NotImplementedException(); } 
            } 

            public Object AsyncState { 
                get { throw new NotImplementedException(); }
            }

            public bool CompletedSynchronously { 
                get { throw new NotImplementedException(); }
            } 
        } 

        static FileSystemWatcher() { 
            notifyFiltersValidMask = 0;
            foreach (int enumValue in Enum.GetValues(typeof(NotifyFilters)))
                notifyFiltersValidMask |= enumValue;
        } 

        ///  
        /// Initializes a new instance of the  class. 
        /// 
        public FileSystemWatcher() { 
            this.directory = String.Empty;
            this.filter = "*.*";
        }
 
        /// 
        ///     
        ///       Initializes a new instance of the  class, 
        ///       given the specified directory to monitor.
        ///     
        /// 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public FileSystemWatcher(string path) : this(path, "*.*") { 
        }
 
 
        /// 
        ///     
        ///       Initializes a new instance of the  class,
        ///       given the specified directory and type of files to monitor.
        ///    
        ///  
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)] 
        public FileSystemWatcher(string path, string filter) { 
            if (path == null)
                throw new ArgumentNullException("path"); 

            if (filter == null)
                throw new ArgumentNullException("filter");
 
            // Early check for directory parameter so that an exception can be thrown as early as possible.
            if (path.Length == 0 || !Directory.Exists(path)) 
                throw new ArgumentException(SR.GetString(SR.InvalidDirName, path)); 

            this.directory = path; 
            this.filter = filter;
        }

        ///  
        ///    
        ///       Gets or sets the type of changes to watch for. 
        ///     
        /// 
        [ 
        DefaultValue(defaultNotifyFilters),
        IODescription(SR.FSW_ChangedFilter)
        ]
        public NotifyFilters NotifyFilter { 
            get {
                return notifyFilters; 
            } 
            set {
                if (((int) value & ~notifyFiltersValidMask) != 0) 
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(NotifyFilters));

                if (notifyFilters != value) {
                    notifyFilters = value; 

                    Restart(); 
                } 
            }
        } 

        /// 
        ///    Gets or sets a value indicating whether the component is enabled.
        ///  
        [
        DefaultValue(false), 
        IODescription(SR.FSW_Enabled) 
        ]
        public bool EnableRaisingEvents { 
            get {
                return enabled;
            }
            set { 
                if (enabled == value) {
                    return; 
                } 

                enabled = value; 

                if (!IsSuspended()) {
                    if (enabled) {
                        StartRaisingEvents(); 
                    }
                    else { 
                        StopRaisingEvents(); 
                    }
                } 
            }
        }

        ///  
        ///    Gets or sets the filter string, used to determine what files are monitored in a directory.
        ///  
        [ 
        DefaultValue("*.*"),
        IODescription(SR.FSW_Filter), 
        TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign),
        SettingsBindable(true),
        ]
        public string Filter { 
            get {
                return filter; 
            } 
            set {
                if (String.IsNullOrEmpty(value)) { 
                    value = "*.*";
                }
                if (String.Compare(filter, value, StringComparison.OrdinalIgnoreCase) != 0) {
                    filter = value; 
                }
            } 
        } 

        ///  
        ///    
        ///       Gets or sets a
        ///       value indicating whether subdirectories within the specified path should be monitored.
        ///     
        /// 
        [ 
        DefaultValue(false), 
        IODescription(SR.FSW_IncludeSubdirectories)
        ] 
        public bool IncludeSubdirectories {
            get {
                return includeSubdirectories;
            } 
            set {
                if (includeSubdirectories != value) { 
                    includeSubdirectories = value; 

                    Restart(); 
                }
            }
        }
 
        /// 
        ///    Gets or 
        ///       sets the size of the internal buffer. 
        /// 
        [ 
        Browsable(false),
        DefaultValue(8192)
        ]
        public int InternalBufferSize { 
            get {
                return internalBufferSize; 
            } 
            set {
                if (internalBufferSize != value) { 
                    if (value < 4096) {
                        value = 4096;
                    }
 
                    internalBufferSize = value;
 
                    Restart(); 
                }
            } 
        }

        private bool IsHandleInvalid {
            get { 
                return (directoryHandle == null || directoryHandle.IsInvalid);
            } 
        } 

        ///  
        ///    Gets or sets the path of the directory to watch.
        /// 
        [
        DefaultValue(""), 
        IODescription(SR.FSW_Path),
        Editor("System.Diagnostics.Design.FSWPathEditor, " + AssemblyRef.SystemDesign, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), 
        TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign), 
        SettingsBindable(true)
        ] 
        public string Path {
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            get { 
                return directory;
            } 
            [ResourceExposure(ResourceScope.Machine)] 
            [ResourceConsumption(ResourceScope.Machine)]
            set { 
                value = (value == null) ? string.Empty : value;
                if (String.Compare(directory, value, StringComparison.OrdinalIgnoreCase) != 0) {
                    if (DesignMode) {
                        // Don't check the path if in design mode, try to do simple syntax check 
                        if (value.IndexOfAny(FileSystemWatcher.wildcards) != -1 || value.IndexOfAny(System.IO.Path.GetInvalidPathChars()) != -1) {
                            throw new ArgumentException(SR.GetString(SR.InvalidDirName, value)); 
                        } 
                    }
                    else { 
                        if (!Directory.Exists(value))
                            throw new ArgumentException(SR.GetString(SR.InvalidDirName, value));
                    }
                    directory = value; 
                    readGranted = false;
                    Restart(); 
                } 
            }
        } 

        /// 
        /// 
        ///  
        [Browsable(false)]
        public override ISite Site { 
            get { 
                return base.Site;
            } 
            set {
                base.Site = value;

                // set EnableRaisingEvents to true at design time so the user 
                // doesn't have to manually. We can't do this in
                // the constructor because in code it should 
                // default to false. 
                if (Site != null && Site.DesignMode)
                    EnableRaisingEvents = true; 
            }
        }

        ///  
        ///    
        ///       Gets or sets the object used to marshal the event handler calls issued as a 
        ///       result of a directory change. 
        ///    
        ///  
        [
        Browsable(false),
        DefaultValue(null),
        IODescription(SR.FSW_SynchronizingObject) 
        ]
        public ISynchronizeInvoke SynchronizingObject { 
            get { 
                if (this.synchronizingObject == null && DesignMode) {
                    IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost)); 
                    if (host != null) {
                        object baseComponent = host.RootComponent;
                        if (baseComponent != null && baseComponent is ISynchronizeInvoke)
                            this.synchronizingObject = (ISynchronizeInvoke)baseComponent; 
                    }
                } 
 
                return this.synchronizingObject;
            } 

            set {
                this.synchronizingObject = value;
            } 
        }
 
        ///  
        ///    
        ///       Occurs when a file or directory in the specified  
        ///       is changed.
        ///    
        /// 
        [IODescription(SR.FSW_Changed)] 
        public event FileSystemEventHandler Changed {
            add { 
                onChangedHandler += value; 
            }
            remove { 
                onChangedHandler -= value;
            }
        }
 
        /// 
        ///     
        ///       Occurs when a file or directory in the specified  
        ///       is created.
        ///     
        /// 
        [IODescription(SR.FSW_Created)]
        public event FileSystemEventHandler Created {
            add { 
                onCreatedHandler += value;
            } 
            remove { 
                onCreatedHandler -= value;
            } 
        }

        /// 
        ///     
        ///       Occurs when a file or directory in the specified 
        ///       is deleted. 
        ///     
        /// 
        [IODescription(SR.FSW_Deleted)] 
        public event FileSystemEventHandler Deleted {
            add{
                onDeletedHandler += value;
            } 
            remove {
                onDeletedHandler -= value; 
            } 
        }
 
        /// 
        ///    
        ///       Occurs when the internal buffer overflows.
        ///     
        /// 
        [Browsable(false)] 
        public event ErrorEventHandler Error { 
            add {
                onErrorHandler += value; 
            }
            remove {
                onErrorHandler -= value;
            } 
        }
 
        ///  
        ///    
        ///       Occurs when a file or directory in the specified  
        ///       is renamed.
        ///    
        /// 
        [IODescription(SR.FSW_Renamed)] 
        public event RenamedEventHandler Renamed {
            add { 
                onRenamedHandler += value; 
            }
            remove { 
                onRenamedHandler -= value;
            }
        }
 
        /// 
        ///    Notifies the object that initialization is beginning and tells it to standby. 
        ///  
        public void BeginInit() {
            bool oldEnabled = enabled; 
            StopRaisingEvents();
            enabled = oldEnabled;
            initializing = true;
        } 

        ///  
        ///     Callback from thread pool. 
        /// 
        ///  
        private unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped  * overlappedPointer) {
            Overlapped overlapped = Overlapped.Unpack(overlappedPointer);
            FSWAsyncResult asyncResult = (FSWAsyncResult) overlapped.AsyncResult;
            IntPtr bufferPtr = GCHandle.Alloc(asyncResult.buffer, GCHandleType.Pinned).AddrOfPinnedObject(); 

            try { 
                if (stopListening) { 
                    return;
                } 

                lock(this) {
                    if (errorCode != 0) {
                        if (errorCode == 995 /* ERROR_OPERATION_ABORTED */) { 
                            //Win2000 inside a service the first completion status is false
                            //cannot return without monitoring again. 
                            //Because this return statement is inside a try/finally block, 
                            //the finally block will execute. It does restart the monitoring.
                            return; 
                        }
                        else {
                            OnError(new ErrorEventArgs(new Win32Exception((int)errorCode)));
                            EnableRaisingEvents = false; 
                            return;
                        } 
                    } 

                    // Ignore any events that occurred before this "session", 
                    // so we don't get changed or error events after we
                    // told FSW to stop.
                    if (asyncResult.session != currentSession)
                        return; 

                    if (numBytes == 0) { 
                        NotifyInternalBufferOverflowEvent(); 
                    }
                    else {  // Else, parse each of them and notify appropriate delegates 

                        /******
                            Format for the buffer is the following C struct:
 
                            typedef struct _FILE_NOTIFY_INFORMATION {
                               DWORD NextEntryOffset; 
                               DWORD Action; 
                               DWORD FileNameLength;
                               WCHAR FileName[1]; 
                            } FILE_NOTIFY_INFORMATION;

                            NOTE1: FileNameLength is length in bytes.
                            NOTE2: The Filename is a Unicode string that's NOT NULL terminated. 
                            NOTE3: A NextEntryOffset of zero means that it's the last entry
                        *******/ 
 
                        // Parse the file notify buffer
                        int offset = 0; 
                        int nextOffset, action, nameLength;
                        string oldName = null;

                        do { 
                            // Get next offset
                            nextOffset = Marshal.ReadInt32((IntPtr)((long)bufferPtr + offset)); 
 
                            // Get change flag
                            action = Marshal.ReadInt32((IntPtr)((long)bufferPtr + offset + 4)); 

                            // Get filename length (in bytes)
                            nameLength = Marshal.ReadInt32((IntPtr)((long)bufferPtr + offset + 8));
 
                            string name = Marshal.PtrToStringUni((IntPtr)((long)bufferPtr + offset + 12), nameLength / 2);
 
 
                            /* A slightly convoluted piece of code follows.  Here's what's happening:
 
                               We wish to collapse the poorly done rename notifications from the
                               ReadDirectoryChangesW API into a nice rename event. So to do that,
                               it's assumed that a FILE_ACTION_RENAMED_OLD_NAME will be followed
                               immediately by a FILE_ACTION_RENAMED_NEW_NAME in the buffer, which is 
                               all that the following code is doing.
 
                               On a FILE_ACTION_RENAMED_OLD_NAME, it asserts that no previous one existed 
                               and saves its name.  If there are no more events in the buffer, it'll
                               assert and fire a RenameEventArgs with the Name field null. 

                               If a NEW_NAME action comes in with no previous OLD_NAME, we assert and fire
                               a rename event with the OldName field null.
 
                               If the OLD_NAME and NEW_NAME actions are indeed there one after the other,
                               we'll fire the RenamedEventArgs normally and clear oldName. 
 
                               If the OLD_NAME is followed by another action, we assert and then fire the
                               rename event with the Name field null and then fire the next action. 

                               In case it's not a OLD_NAME or NEW_NAME action, we just fire the event normally.

                               (Phew!) 
                             */
 
                            // If the action is RENAMED_FROM, save the name of the file 
                            if (action == Direct.FILE_ACTION_RENAMED_OLD_NAME) {
                                Debug.Assert(oldName == null, "FileSystemWatcher: Two FILE_ACTION_RENAMED_OLD_NAME " + 
                                                              "in a row!  [" + oldName + "], [ " + name + "]");

                                oldName = name;
                            } 
                            else if (action == Direct.FILE_ACTION_RENAMED_NEW_NAME) {
                                if (oldName != null) { 
                                    NotifyRenameEventArgs(WatcherChangeTypes.Renamed, name, oldName); 
                                    oldName = null;
                                } 
                                else {
                                    Debug.Assert(false, "FileSystemWatcher: FILE_ACTION_RENAMED_NEW_NAME with no" +
                                                                  "old name! [ " + name + "]");
 
                                    NotifyRenameEventArgs(WatcherChangeTypes.Renamed, name, oldName);
                                    oldName = null; 
                                } 
                            }
                            else { 
                                if (oldName != null) {
                                    Debug.Assert(false, "FileSystemWatcher: FILE_ACTION_RENAMED_OLD_NAME with no" +
                                                                  "new name!  [" + oldName + "]");
 
                                    NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName);
                                    oldName = null; 
                                } 

                                // Notify each file of change 
                                NotifyFileSystemEventArgs(action, name);

                            }
 
                            offset += nextOffset;
                        } while (nextOffset != 0); 
 
                        if (oldName != null) {
                            Debug.Assert(false, "FileSystemWatcher: FILE_ACTION_RENAMED_OLD_NAME with no" + 
                                                          "new name!  [" + oldName + "]");

                            NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName);
                            oldName = null; 
                        }
                    } 
                } 
            }
            finally { 
                Overlapped.Free(overlappedPointer);
                if (!stopListening && !runOnce) {
                    Monitor(asyncResult.buffer);
                } 
                }                                    		
            } 
 
        /// 
        ///  
        protected override void Dispose(bool disposing) {
            try
            {
                if (disposing) 
                {
                //Stop raising events cleans up managed and 
                //unmanaged resources. 
                StopRaisingEvents();
 
                // Clean up managed resources
                onChangedHandler = null;
                onCreatedHandler = null;
                onDeletedHandler = null; 
                onRenamedHandler = null;
                onErrorHandler = null; 
                readGranted = false; 
            }
                else 
                {
                stopListening = true;

                // Clean up unmanaged resources 
                    if (!IsHandleInvalid)
                    { 
                    directoryHandle.Close(); 
                }
            } 
            }
            finally
            {
            this.disposed = true; 
            base.Dispose(disposing);
        } 
        } 

        ///  
        ///    
        ///       Notifies the object that initialization is complete.
        ///    
        ///  
        public void EndInit() {
            initializing = false; 
            // Unless user told us NOT to start after initialization, we'll start listening 
            // to events
            if (directory.Length != 0 && enabled == true) 
                StartRaisingEvents();
        }

 
        /// 
        ///     Returns true if the component is either in a Begin/End Init block or in design mode. 
        ///  
        // 
        // 
        private bool IsSuspended() {
            return initializing || DesignMode;
        }
 
        /// 
        ///     Sees if the name given matches the name filter we have. 
        ///  
        /// 
        private bool MatchPattern(string relativePath) { 
            string name = System.IO.Path.GetFileName(relativePath);
            if (name != null)
                return PatternMatcher.StrictMatchPattern(filter.ToUpper(CultureInfo.InvariantCulture), name.ToUpper(CultureInfo.InvariantCulture));
            else 
                return false;
        } 
 
        /// 
        ///     Calls native API and sets up handle with the directory change API. 
        /// 
        /// 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private unsafe void Monitor(byte[] buffer) {
            if (!enabled || IsHandleInvalid) { 
                return; 
            }
 
            Overlapped overlapped = new Overlapped();
            if (buffer == null) {
                try {
                    buffer = new byte[internalBufferSize]; 
                }
                catch (OutOfMemoryException) { 
                        throw new OutOfMemoryException(SR.GetString(SR.BufferSizeTooLarge, internalBufferSize.ToString(CultureInfo.CurrentCulture))); 
                }
            } 

            // Just to pass "session" counter to callback
            FSWAsyncResult asyncResult = new FSWAsyncResult();
            asyncResult.session = currentSession; 
            asyncResult.buffer = buffer;
            overlapped.AsyncResult = asyncResult; 
            NativeOverlapped* overlappedPointer = overlapped.Pack(new IOCompletionCallback(this.CompletionStatusChanged), buffer); 
            IntPtr bufferPtr = GCHandle.Alloc(buffer, GCHandleType.Pinned).AddrOfPinnedObject();
 
            int size;
            bool ok = false;

            try { 
                // There could be a ---- in user code between calling StopRaisingEvents (where we close the handle)
                // and when we get here from CompletionStatusChanged. 
                // We might need to take a lock to prevent ---- absolutely, instead just catch 
                // ObjectDisposedException from SafeHandle in case it is disposed
                if (!IsHandleInvalid) { 
                    // An interrupt is possible here
                    ok = UnsafeNativeMethods.ReadDirectoryChangesW(directoryHandle,
                                                       new HandleRef(this, bufferPtr),
                                                       internalBufferSize, 
                                                       includeSubdirectories ? 1 : 0,
                                                       (int)notifyFilters, 
                                                       out size, 
                                                       overlappedPointer,
                                                       NativeMethods.NullHandleRef); 
                }
            }
            catch (ObjectDisposedException ) { //Ignore
                Debug.Assert(IsHandleInvalid, "ObjectDisposedException from something other than SafeHandle?"); 
            }
            catch (ArgumentNullException ) { //Ignore 
                Debug.Assert(IsHandleInvalid, "ArgumentNullException from something other than SafeHandle?"); 
            }
            finally { 
                if (! ok) {
                    Overlapped.Free(overlappedPointer);

                    // If the handle was for some reason changed or closed during this call, then don't throw an 
                    // exception.  Else, it's a valid error.
                    if (!IsHandleInvalid) { 
                        OnError(new ErrorEventArgs(new Win32Exception())); 
                    }
                } 
            }
        }

        ///  
        ///     Raises the event to each handler in the list.
        ///  
        ///  
        private void NotifyFileSystemEventArgs(int action, string name) {
            if (!MatchPattern(name)) { 
                return;
            }

            switch (action) { 
                case Direct.FILE_ACTION_ADDED:
                    OnCreated(new FileSystemEventArgs(WatcherChangeTypes.Created, directory, name)); 
                    break; 
                case Direct.FILE_ACTION_REMOVED:
                    OnDeleted(new FileSystemEventArgs(WatcherChangeTypes.Deleted, directory, name)); 
                    break;
                case Direct.FILE_ACTION_MODIFIED:
                    OnChanged(new FileSystemEventArgs(WatcherChangeTypes.Changed, directory, name));
                    break; 

                default: 
                    Debug.Fail("Unknown FileSystemEvent action type!  Value: "+action); 
                    break;
            } 
        }

        /// 
        ///     Raises the event to each handler in the list. 
        /// 
        ///  
        private void NotifyInternalBufferOverflowEvent() { 
            InternalBufferOverflowException ex = new InternalBufferOverflowException(SR.GetString(SR.FSW_BufferOverflow, directory));
 
            ErrorEventArgs errevent = new ErrorEventArgs(ex);

            OnError(errevent);
        } 

        ///  
        ///     Raises the event to each handler in the list. 
        /// 
        ///  
        private void NotifyRenameEventArgs(WatcherChangeTypes action, string name, string oldName) {
            //filter if neither new name or old name are a match a specified pattern
            if (!MatchPattern(name) && !MatchPattern(oldName)) {
                return; 
            }
 
            RenamedEventArgs renevent = new RenamedEventArgs(action, directory, name, oldName); 
            OnRenamed(renevent);
        } 

        /// 
        ///    
        ///       Raises the  event. 
        ///    
        ///  
        [SuppressMessage("Microsoft.Security","CA2109:ReviewVisibleEventHandlers", MessageId="0#", Justification="Changing from protected to private would be a breaking change")] 
        protected void OnChanged(FileSystemEventArgs e) {
            // To avoid ---- between remove handler and raising the event 
            FileSystemEventHandler changedHandler = onChangedHandler;

            if (changedHandler != null) {
                if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) 
                    this.SynchronizingObject.BeginInvoke(changedHandler, new object[]{this, e});
                else 
                   changedHandler(this, e); 
            }
        } 

        /// 
        ///    
        ///       Raises the  event. 
        ///    
        ///  
        [SuppressMessage("Microsoft.Security","CA2109:ReviewVisibleEventHandlers", MessageId="0#", Justification="Changing from protected to private would be a breaking change")] 
        protected void OnCreated(FileSystemEventArgs e) {
            // To avoid ---- between remove handler and raising the event 
            FileSystemEventHandler createdHandler = onCreatedHandler;
            if (createdHandler != null) {
                if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
                    this.SynchronizingObject.BeginInvoke(createdHandler, new object[]{this, e}); 
                else
                   createdHandler(this, e); 
            } 
        }
 
        /// 
        ///    
        ///       Raises the  event.
        ///     
        /// 
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")] 
        protected void OnDeleted(FileSystemEventArgs e) { 
            // To avoid ---- between remove handler and raising the event
            FileSystemEventHandler deletedHandler = onDeletedHandler; 
            if (deletedHandler != null) {
                if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
                    this.SynchronizingObject.BeginInvoke(deletedHandler, new object[]{this, e});
                else 
                   deletedHandler(this, e);
            } 
        } 

        ///  
        ///    
        ///       Raises the  event.
        ///    
        ///  
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")]
        protected void OnError(ErrorEventArgs e) { 
            // To avoid ---- between remove handler and raising the event 
            ErrorEventHandler errorHandler = onErrorHandler;
            if (errorHandler != null) { 
                if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
                    this.SynchronizingObject.BeginInvoke(errorHandler, new object[]{this, e});
                else
                   errorHandler(this, e); 
            }
        } 
 
        /// 
        ///     Internal method used for synchronous notification. 
        /// 
        /// 
        private void OnInternalFileSystemEventArgs(object sender, FileSystemEventArgs e) {
            lock (this) { 
                // Only change the state of the changed result if it doesn't contain a previous one.
                if (isChanged != true) { 
                    changedResult = new WaitForChangedResult(e.ChangeType, e.Name, false); 
                    isChanged = true;
                    System.Threading.Monitor.Pulse(this); 
                }
            }
        }
 
        /// 
        ///     Internal method used for synchronous notification. 
        ///  
        /// 
        private void OnInternalRenameEventArgs(object sender, RenamedEventArgs e) { 
            lock (this) {
                // Only change the state of the changed result if it doesn't contain a previous one.
                if (isChanged != true) {
                    changedResult = new WaitForChangedResult(e.ChangeType, e.Name, e.OldName, false); 
                    isChanged = true;
                    System.Threading.Monitor.Pulse(this); 
                } 
            }
        } 

        /// 
        ///    
        ///       Raises the  event. 
        ///    
        ///  
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")] 
        protected void OnRenamed(RenamedEventArgs e) {
            RenamedEventHandler renamedHandler = onRenamedHandler; 
            if (renamedHandler != null) {
                if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
                    this.SynchronizingObject.BeginInvoke(renamedHandler, new object[]{this, e});
                else 
                   renamedHandler(this, e);
            } 
        } 

        ///  
        ///     Stops and starts this object.
        /// 
        /// 
        private void Restart() { 
            if ((!IsSuspended()) && enabled) {
                StopRaisingEvents(); 
                StartRaisingEvents(); 
            }
        } 

        /// 
        ///    
        ///       Starts monitoring the specified directory. 
        ///    
        ///  
        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private void StartRaisingEvents() { 
            //Cannot allocate the directoryHandle and the readBuffer if the object has been disposed; finalization has been suppressed.
            if (this.disposed)
                throw new ObjectDisposedException(GetType().Name);
 
            try {
                new EnvironmentPermission(PermissionState.Unrestricted).Assert(); 
                if (Environment.OSVersion.Platform != PlatformID.Win32NT) { 
                    throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequired));
                } 
            }
            finally {
                CodeAccessPermission.RevertAssert();
            } 

            // If we're called when "Initializing" is true, set enabled to true 
            if (IsSuspended()) { 
                enabled = true;
                return; 
            }

            if (!readGranted) {
                string fullPath; 
                // Consider asserting path discovery permission here.
                fullPath = System.IO.Path.GetFullPath(directory); 
 
                FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.Read, fullPath);
                permission.Demand(); 
                readGranted = true;
            }

 
            // If we're attached, don't do anything.
            if (!IsHandleInvalid) { 
                return; 
            }
 
            // Create handle to directory being monitored
            directoryHandle = NativeMethods.CreateFile(directory,            // Directory name
                                UnsafeNativeMethods.FILE_LIST_DIRECTORY,           // access (read-write) mode
                                UnsafeNativeMethods.FILE_SHARE_READ | 
                                    UnsafeNativeMethods.FILE_SHARE_DELETE |
                                    UnsafeNativeMethods.FILE_SHARE_WRITE,          // share mode 
                                null,                                              // security descriptor 
                                UnsafeNativeMethods.OPEN_EXISTING,                 // how to create
                                UnsafeNativeMethods.FILE_FLAG_BACKUP_SEMANTICS | 
                                    UnsafeNativeMethods.FILE_FLAG_OVERLAPPED,      // file attributes
                                new SafeFileHandle(IntPtr.Zero, false)             // file with attributes to copy
                            );
 
            if (IsHandleInvalid) {
                throw new FileNotFoundException(SR.GetString(SR.FSW_IOError, directory)); 
            } 

            stopListening = false; 
            // Start ignoring all events that were initiated before this.
            Interlocked.Increment(ref currentSession);

            // Attach handle to thread pool 

            //SECREVIEW: At this point at least FileIOPermission has already been demanded. 
            SecurityPermission secPermission = new SecurityPermission(PermissionState.Unrestricted); 
            secPermission.Assert();
            try { 
                ThreadPool.BindHandle(directoryHandle);
            }
            finally {
                SecurityPermission.RevertAssert(); 
            }
            enabled = true; 
 
            // Setup IO completion port
            Monitor(null); 
        }

        /// 
        ///     
        ///       Stops monitoring the specified directory.
        ///     
        ///  
        private void StopRaisingEvents() {
            if (IsSuspended()) { 
                enabled = false;
                return;
            }
 
            // If we're not attached, do nothing.
            if (IsHandleInvalid) { 
                return; 
            }
 
            // Close directory handle
            // This operation doesn't need to be atomic because the API will deal with a closed
            // handle appropriately.
            // Ensure that the directoryHandle is set to INVALID_HANDLE before closing it, so that 
            // the Monitor() can shutdown appropriately.
            stopListening = true; 
            directoryHandle.Close(); 
            directoryHandle = null;
 

            // Start ignoring all events occurring after this.
            Interlocked.Increment(ref currentSession);
 
            // Set enabled to false
            enabled = false; 
        } 

        ///  
        ///    
        ///       A synchronous method that returns a structure that
        ///       contains specific information on the change that occurred, given the type
        ///       of change that you wish to monitor. 
        ///    
        ///  
        public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) { 
            return WaitForChanged(changeType, -1);
        } 

        /// 
        ///    
        ///       A synchronous 
        ///       method that returns a structure that contains specific information on the change that occurred, given the
        ///       type of change that you wish to monitor and the time (in milliseconds) to wait before timing out. 
        ///     
        /// 
        public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) { 
            FileSystemEventHandler dirHandler = new FileSystemEventHandler(this.OnInternalFileSystemEventArgs);
            RenamedEventHandler renameHandler = new RenamedEventHandler(this.OnInternalRenameEventArgs);

            this.isChanged = false; 
            this.changedResult = WaitForChangedResult.TimedOutResult;
 
            // Register the internal event handler from the given change types. 
            if ((changeType & WatcherChangeTypes.Created) != 0) {
                this.Created += dirHandler; 
            }
            if ((changeType & WatcherChangeTypes.Deleted) != 0) {
                this.Deleted += dirHandler;
            } 
            if ((changeType & WatcherChangeTypes.Changed) != 0) {
                this.Changed += dirHandler; 
            } 
            if ((changeType & WatcherChangeTypes.Renamed) != 0) {
                this.Renamed += renameHandler; 
            }

            // Save the Enabled state of this component to revert back to it later (if needed).
            bool savedEnabled = EnableRaisingEvents; 
            if (savedEnabled == false) {
                runOnce = true; 
                EnableRaisingEvents = true; 
            }
 
            // For each thread entering this wait loop, addref it and wait.  When the last one
            // exits, reset the waiterObject.
            WaitForChangedResult retVal = WaitForChangedResult.TimedOutResult;
            lock (this) { 
                if (timeout == -1) {
                    while (!isChanged) { 
                        System.Threading.Monitor.Wait(this); 
                    }
                } 
                else {
                    System.Threading.Monitor.Wait(this, timeout, true);
                }
 
                retVal = changedResult;
            } 
 
            // Revert the Enabled flag to its previous state.
            EnableRaisingEvents = savedEnabled; 
            runOnce = false;

            // Decouple the event handlers added above.
            if ((changeType & WatcherChangeTypes.Created) != 0) { 
                this.Created -= dirHandler;
            } 
            if ((changeType & WatcherChangeTypes.Deleted) != 0) { 
                this.Deleted -= dirHandler;
            } 
            if ((changeType & WatcherChangeTypes.Changed) != 0) {
                this.Changed -= dirHandler;
            }
            if ((changeType & WatcherChangeTypes.Renamed) != 0) { 
                this.Renamed -= renameHandler;
            } 
 
            // Return the struct.
            return retVal; 
        }
    }

    ///  
    ///    Helper class to hold to N/Direct call declaration and flags.
    ///  
    [ 
        System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
    ] 
    internal static class Direct {
        // All possible action flags
        public const int FILE_ACTION_ADDED            = 1;
        public const int FILE_ACTION_REMOVED          = 2; 
        public const int FILE_ACTION_MODIFIED         = 3;
        public const int FILE_ACTION_RENAMED_OLD_NAME = 4; 
        public const int FILE_ACTION_RENAMED_NEW_NAME = 5; 

 
        // All possible notifications flags
        public const int FILE_NOTIFY_CHANGE_FILE_NAME    = 0x00000001;
        public const int FILE_NOTIFY_CHANGE_DIR_NAME     = 0x00000002;
        public const int FILE_NOTIFY_CHANGE_NAME         = 0x00000003; 
        public const int FILE_NOTIFY_CHANGE_ATTRIBUTES   = 0x00000004;
        public const int FILE_NOTIFY_CHANGE_SIZE         = 0x00000008; 
        public const int FILE_NOTIFY_CHANGE_LAST_WRITE   = 0x00000010; 
        public const int FILE_NOTIFY_CHANGE_LAST_ACCESS  = 0x00000020;
        public const int FILE_NOTIFY_CHANGE_CREATION     = 0x00000040; 
        public const int FILE_NOTIFY_CHANGE_SECURITY     = 0x00000100;
    }
}
 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK