Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Services / IO / System / IO / FileSystemWatcher.cs / 1 / 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; 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; ////// [ DefaultEvent("Changed"), // Disabling partial trust scenarios PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust"), IODescription(SR.FileSystemWatcherDesc) ] public class FileSystemWatcher : Component, ISupportInitialize { ///Listens to the system directory change notifications and /// raises events when a directory or file within a directory changes. ////// 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; 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; } ////// public FileSystemWatcher() { this.directory = String.Empty; this.filter = "*.*"; } ///Initializes a new instance of the ///class. /// [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public FileSystemWatcher(string path) : this(path, "*.*") { } ////// Initializes a new instance of the ///class, /// given the specified directory 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; } ////// Initializes a new instance of the ///class, /// given the specified directory and type of files to monitor. /// /// [ 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 the type of changes to watch for. /// ////// [ 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 a value indicating whether the component is enabled. ////// [ DefaultValue("*.*"), IODescription(SR.FSW_Filter), TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign), RecommendedAsConfigurable(true), ] public string Filter { get { return filter; } set { if (value == null || value == String.Empty) { value = "*.*"; } if (String.Compare(filter, value, StringComparison.OrdinalIgnoreCase) != 0) { filter = value; } } } ///Gets or sets the filter string, used to determine what files are monitored in a directory. ////// [ DefaultValue(false), IODescription(SR.FSW_IncludeSubdirectories) ] public bool IncludeSubdirectories { get { return includeSubdirectories; } set { if (includeSubdirectories != value) { includeSubdirectories = value; Restart(); } } } ////// Gets or sets a /// value indicating whether subdirectories within the specified path should be monitored. /// ////// [ 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 size of the internal buffer. ////// [ 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), RecommendedAsConfigurable(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(); } } } ///Gets or sets the path of the directory to watch. ////// /// [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; } } ////// [ 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; } } ////// Gets or sets the object used to marshal the event handler calls issued as a /// result of a directory change. /// ////// [IODescription(SR.FSW_Changed)] public event FileSystemEventHandler Changed { add { onChangedHandler += value; } remove { onChangedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is changed. /// /// [IODescription(SR.FSW_Created)] public event FileSystemEventHandler Created { add { onCreatedHandler += value; } remove { onCreatedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is created. /// /// [IODescription(SR.FSW_Deleted)] public event FileSystemEventHandler Deleted { add{ onDeletedHandler += value; } remove { onDeletedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is deleted. /// /// [Browsable(false)] public event ErrorEventHandler Error { add { onErrorHandler += value; } remove { onErrorHandler -= value; } } ////// Occurs when the internal buffer overflows. /// ////// [IODescription(SR.FSW_Renamed)] public event RenamedEventHandler Renamed { add { onRenamedHandler += value; } remove { onRenamedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is renamed. /// /// public void BeginInit() { bool oldEnabled = enabled; StopRaisingEvents(); enabled = oldEnabled; initializing = true; } ///Notifies the object that initialization is beginning and tells it to standby. ////// Callback from thread pool. /// ///private unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped * overlappedPointer) { Overlapped overlapped = Overlapped.Unpack(overlappedPointer); ulong bufferPtrTemp = ((ulong)overlapped.OffsetHigh) << 32; bufferPtrTemp = bufferPtrTemp | ((ulong)(uint)overlapped.OffsetLow); IntPtr bufferPtr = (IntPtr)bufferPtrTemp; FSWAsyncResult asyncResult = (FSWAsyncResult) overlapped.AsyncResult; 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) { if (bufferPtr != (IntPtr)0) Marshal.FreeHGlobal(bufferPtr); } else { Monitor(bufferPtr); } } } /// /// protected override void Dispose(bool disposing) { 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(); } } this.disposed = true; base.Dispose(disposing); } ////// 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(); } ////// Notifies the object that initialization is complete. /// ////// 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. /// ///private unsafe void Monitor(IntPtr bufferPtr) { if (!enabled || IsHandleInvalid) { return; } Overlapped overlapped = new Overlapped(); if (bufferPtr == (IntPtr) 0) { try { bufferPtr = Marshal.AllocHGlobal(internalBufferSize); } catch (OutOfMemoryException) { throw new OutOfMemoryException(SR.GetString(SR.BufferSizeTooLarge, internalBufferSize.ToString(CultureInfo.CurrentCulture))); } } ulong bufferPtrTemp = (ulong)bufferPtr; overlapped.OffsetHigh = (int)(bufferPtrTemp >> 32); overlapped.OffsetLow = (int)(bufferPtrTemp); // Just to pass "session" counter to callback FSWAsyncResult asyncResult = new FSWAsyncResult(); asyncResult.session = currentSession; overlapped.AsyncResult = asyncResult; NativeOverlapped* overlappedPointer = overlapped.Pack(new IOCompletionCallback(this.CompletionStatusChanged), currentSession); 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); Marshal.FreeHGlobal(bufferPtr); // 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); } /// /// 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. /// /// 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. /// /// 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. /// /// 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); } } ////// Raises the ///event. /// /// 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); } } } /// /// 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); } } ////// Raises the ///event. /// /// Stops and starts this object. /// ///private void Restart() { if ((!IsSuspended()) && enabled) { StopRaisingEvents(); StartRaisingEvents(); } } /// /// [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((IntPtr)0); } ////// Starts 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 appropriatelly. // Ensure that the directoryHandle is set to INVALID_HANDLE before closing it, so that // the Monitor() can shutdown appropriatelly. stopListening = true; directoryHandle.Close(); directoryHandle = null; // Start ignoring all events occurring after this. Interlocked.Increment(ref currentSession); // Set enabled to false enabled = false; } ////// Stops monitoring the specified directory. /// ////// 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. /// ////// 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; } } ////// 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. /// ////// 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. //------------------------------------------------------------------------------ //// 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; 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; ////// [ DefaultEvent("Changed"), // Disabling partial trust scenarios PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust"), IODescription(SR.FileSystemWatcherDesc) ] public class FileSystemWatcher : Component, ISupportInitialize { ///Listens to the system directory change notifications and /// raises events when a directory or file within a directory changes. ////// 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; 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; } ////// public FileSystemWatcher() { this.directory = String.Empty; this.filter = "*.*"; } ///Initializes a new instance of the ///class. /// [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public FileSystemWatcher(string path) : this(path, "*.*") { } ////// Initializes a new instance of the ///class, /// given the specified directory 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; } ////// Initializes a new instance of the ///class, /// given the specified directory and type of files to monitor. /// /// [ 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 the type of changes to watch for. /// ////// [ 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 a value indicating whether the component is enabled. ////// [ DefaultValue("*.*"), IODescription(SR.FSW_Filter), TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign), RecommendedAsConfigurable(true), ] public string Filter { get { return filter; } set { if (value == null || value == String.Empty) { value = "*.*"; } if (String.Compare(filter, value, StringComparison.OrdinalIgnoreCase) != 0) { filter = value; } } } ///Gets or sets the filter string, used to determine what files are monitored in a directory. ////// [ DefaultValue(false), IODescription(SR.FSW_IncludeSubdirectories) ] public bool IncludeSubdirectories { get { return includeSubdirectories; } set { if (includeSubdirectories != value) { includeSubdirectories = value; Restart(); } } } ////// Gets or sets a /// value indicating whether subdirectories within the specified path should be monitored. /// ////// [ 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 size of the internal buffer. ////// [ 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), RecommendedAsConfigurable(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(); } } } ///Gets or sets the path of the directory to watch. ////// /// [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; } } ////// [ 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; } } ////// Gets or sets the object used to marshal the event handler calls issued as a /// result of a directory change. /// ////// [IODescription(SR.FSW_Changed)] public event FileSystemEventHandler Changed { add { onChangedHandler += value; } remove { onChangedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is changed. /// /// [IODescription(SR.FSW_Created)] public event FileSystemEventHandler Created { add { onCreatedHandler += value; } remove { onCreatedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is created. /// /// [IODescription(SR.FSW_Deleted)] public event FileSystemEventHandler Deleted { add{ onDeletedHandler += value; } remove { onDeletedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is deleted. /// /// [Browsable(false)] public event ErrorEventHandler Error { add { onErrorHandler += value; } remove { onErrorHandler -= value; } } ////// Occurs when the internal buffer overflows. /// ////// [IODescription(SR.FSW_Renamed)] public event RenamedEventHandler Renamed { add { onRenamedHandler += value; } remove { onRenamedHandler -= value; } } ////// Occurs when a file or directory in the specified ////// is renamed. /// /// public void BeginInit() { bool oldEnabled = enabled; StopRaisingEvents(); enabled = oldEnabled; initializing = true; } ///Notifies the object that initialization is beginning and tells it to standby. ////// Callback from thread pool. /// ///private unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped * overlappedPointer) { Overlapped overlapped = Overlapped.Unpack(overlappedPointer); ulong bufferPtrTemp = ((ulong)overlapped.OffsetHigh) << 32; bufferPtrTemp = bufferPtrTemp | ((ulong)(uint)overlapped.OffsetLow); IntPtr bufferPtr = (IntPtr)bufferPtrTemp; FSWAsyncResult asyncResult = (FSWAsyncResult) overlapped.AsyncResult; 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) { if (bufferPtr != (IntPtr)0) Marshal.FreeHGlobal(bufferPtr); } else { Monitor(bufferPtr); } } } /// /// protected override void Dispose(bool disposing) { 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(); } } this.disposed = true; base.Dispose(disposing); } ////// 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(); } ////// Notifies the object that initialization is complete. /// ////// 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. /// ///private unsafe void Monitor(IntPtr bufferPtr) { if (!enabled || IsHandleInvalid) { return; } Overlapped overlapped = new Overlapped(); if (bufferPtr == (IntPtr) 0) { try { bufferPtr = Marshal.AllocHGlobal(internalBufferSize); } catch (OutOfMemoryException) { throw new OutOfMemoryException(SR.GetString(SR.BufferSizeTooLarge, internalBufferSize.ToString(CultureInfo.CurrentCulture))); } } ulong bufferPtrTemp = (ulong)bufferPtr; overlapped.OffsetHigh = (int)(bufferPtrTemp >> 32); overlapped.OffsetLow = (int)(bufferPtrTemp); // Just to pass "session" counter to callback FSWAsyncResult asyncResult = new FSWAsyncResult(); asyncResult.session = currentSession; overlapped.AsyncResult = asyncResult; NativeOverlapped* overlappedPointer = overlapped.Pack(new IOCompletionCallback(this.CompletionStatusChanged), currentSession); 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); Marshal.FreeHGlobal(bufferPtr); // 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); } /// /// 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. /// /// 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. /// /// 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. /// /// 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); } } ////// Raises the ///event. /// /// 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); } } } /// /// 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); } } ////// Raises the ///event. /// /// Stops and starts this object. /// ///private void Restart() { if ((!IsSuspended()) && enabled) { StopRaisingEvents(); StartRaisingEvents(); } } /// /// [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((IntPtr)0); } ////// Starts 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 appropriatelly. // Ensure that the directoryHandle is set to INVALID_HANDLE before closing it, so that // the Monitor() can shutdown appropriatelly. stopListening = true; directoryHandle.Close(); directoryHandle = null; // Start ignoring all events occurring after this. Interlocked.Increment(ref currentSession); // Set enabled to false enabled = false; } ////// Stops monitoring the specified directory. /// ////// 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. /// ////// 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; } } ////// 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. /// ////// 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RequestChannel.cs
- ProviderUtil.cs
- SqlDependencyListener.cs
- DesignerAutoFormatCollection.cs
- BookmarkUndoUnit.cs
- RenderTargetBitmap.cs
- InvalidStoreProtectionKeyException.cs
- EntityDataSourceQueryBuilder.cs
- DBAsyncResult.cs
- FocusChangedEventArgs.cs
- SignedPkcs7.cs
- SoapIncludeAttribute.cs
- SqlCacheDependency.cs
- TransactionContextManager.cs
- GAC.cs
- XmlSchemaSimpleTypeRestriction.cs
- ExecutedRoutedEventArgs.cs
- ClientCultureInfo.cs
- DefaultTextStoreTextComposition.cs
- InvokerUtil.cs
- XmlDataSourceNodeDescriptor.cs
- XmlSyndicationContent.cs
- DesignTimeVisibleAttribute.cs
- RegisteredScript.cs
- ReflectionTypeLoadException.cs
- SoapAttributeAttribute.cs
- Random.cs
- BamlMapTable.cs
- DateTimeConverter.cs
- SizeConverter.cs
- TimelineGroup.cs
- WindowsListViewGroup.cs
- CrossSiteScriptingValidation.cs
- GridSplitter.cs
- TreeViewBindingsEditorForm.cs
- DrawingGroup.cs
- ImmutableAssemblyCacheEntry.cs
- StandardRuntimeEnumValidatorAttribute.cs
- EUCJPEncoding.cs
- CloseSequenceResponse.cs
- MetadataArtifactLoader.cs
- FolderLevelBuildProviderCollection.cs
- QueuePathDialog.cs
- CompoundFileStorageReference.cs
- DataColumnChangeEvent.cs
- CaseStatement.cs
- CodeMemberProperty.cs
- CryptoProvider.cs
- DataGridViewCellValidatingEventArgs.cs
- AmbientLight.cs
- TextFormatterContext.cs
- StorageRoot.cs
- AddInToken.cs
- SuppressMergeCheckAttribute.cs
- NumericUpDownAcceleration.cs
- MessageEncoder.cs
- XmlLanguageConverter.cs
- ListArgumentProvider.cs
- SHA1CryptoServiceProvider.cs
- TextInfo.cs
- DesignerOptions.cs
- ReadOnlyDataSource.cs
- EmbeddedObject.cs
- RtfControlWordInfo.cs
- TraceHandler.cs
- ObjectContextServiceProvider.cs
- UIElementPropertyUndoUnit.cs
- QueryStringParameter.cs
- CollectionBuilder.cs
- SQLBoolean.cs
- SortDescription.cs
- MetadataPropertyvalue.cs
- XamlBuildTaskServices.cs
- PeerNameResolver.cs
- OrderByExpression.cs
- BindingElementCollection.cs
- ConfigXmlComment.cs
- SerializationFieldInfo.cs
- FileDataSourceCache.cs
- HeaderLabel.cs
- XmlWriterTraceListener.cs
- PackageRelationshipCollection.cs
- DataTableCollection.cs
- mansign.cs
- TextAction.cs
- externdll.cs
- WindowHideOrCloseTracker.cs
- FixedTextBuilder.cs
- CollaborationHelperFunctions.cs
- TypeListConverter.cs
- BorderGapMaskConverter.cs
- CertificateReferenceElement.cs
- DynamicRendererThreadManager.cs
- SingleTagSectionHandler.cs
- RenamedEventArgs.cs
- AsyncStreamReader.cs
- FtpCachePolicyElement.cs
- FixedSOMPageConstructor.cs
- GestureRecognitionResult.cs
- XPathChildIterator.cs