Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Services / IO / System / IO / FileSystemWatcher.cs / 2 / 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;
///
/// 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;
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),
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 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),
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();
}
}
}
///
///
///
[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);
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);
}
///
///
/// 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.
///
///
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 race 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 race 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);
}
///
///
/// Raises the event.
///
///
protected void OnChanged(FileSystemEventArgs e) {
// To avoid race 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 race 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 race 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 race 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.
///
///
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((IntPtr)0);
}
///
///
/// 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 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;
}
///
///
/// 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;
}
}
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RepeatButtonAutomationPeer.cs
- TextAutomationPeer.cs
- TextTreeDeleteContentUndoUnit.cs
- WebScriptEnablingElement.cs
- DtdParser.cs
- DbConnectionPoolOptions.cs
- PageHandlerFactory.cs
- StylusPlugin.cs
- DbReferenceCollection.cs
- UrlPath.cs
- itemelement.cs
- Floater.cs
- BooleanAnimationBase.cs
- DesignOnlyAttribute.cs
- BaseTemplateCodeDomTreeGenerator.cs
- SqlCacheDependencySection.cs
- ClientEventManager.cs
- ListenUriMode.cs
- DataRelationPropertyDescriptor.cs
- PersonalizationStateQuery.cs
- SqlVersion.cs
- LogicalExpressionEditor.cs
- WebPartPersonalization.cs
- CorrelationKey.cs
- CriticalExceptions.cs
- ScriptHandlerFactory.cs
- EnumBuilder.cs
- CultureSpecificCharacterBufferRange.cs
- PrinterUnitConvert.cs
- XmlIncludeAttribute.cs
- ProfileService.cs
- BorderGapMaskConverter.cs
- X509AsymmetricSecurityKey.cs
- TabRenderer.cs
- ToolZone.cs
- BrowserTree.cs
- HashSetEqualityComparer.cs
- MessageSecurityProtocolFactory.cs
- EdmError.cs
- MessagePropertyDescriptionCollection.cs
- GeneralTransform3D.cs
- UIElement3DAutomationPeer.cs
- DataPager.cs
- AmbientLight.cs
- DataGridViewHeaderCell.cs
- ContractCodeDomInfo.cs
- EdmType.cs
- EncoderNLS.cs
- MethodInfo.cs
- IncrementalCompileAnalyzer.cs
- UnauthorizedWebPart.cs
- Condition.cs
- BorderGapMaskConverter.cs
- MouseActionConverter.cs
- PerspectiveCamera.cs
- MetaForeignKeyColumn.cs
- FreezableCollection.cs
- TextProviderWrapper.cs
- EventMappingSettings.cs
- CounterCreationDataCollection.cs
- Configuration.cs
- Update.cs
- MediaEntryAttribute.cs
- XmlEnumAttribute.cs
- AccessedThroughPropertyAttribute.cs
- COAUTHIDENTITY.cs
- DeferredSelectedIndexReference.cs
- ToolboxItemLoader.cs
- WindowsAltTab.cs
- ConnectionsZone.cs
- ToolStripContainer.cs
- TextCompositionManager.cs
- ProxyGenerationError.cs
- CultureTableRecord.cs
- SmtpSection.cs
- WebBaseEventKeyComparer.cs
- DrawingGroupDrawingContext.cs
- RangeValuePattern.cs
- DesignerActionService.cs
- HtmlInputReset.cs
- BuildResultCache.cs
- DataGridViewAccessibleObject.cs
- TreeViewCancelEvent.cs
- FormatStringEditor.cs
- ArrayMergeHelper.cs
- WebPartVerbCollection.cs
- ButtonStandardAdapter.cs
- MouseActionValueSerializer.cs
- RefreshEventArgs.cs
- CodeTypeDelegate.cs
- HtmlMeta.cs
- ObjectHelper.cs
- VisualTreeUtils.cs
- URLString.cs
- querybuilder.cs
- Substitution.cs
- DocumentViewerBase.cs
- DataGridDetailsPresenter.cs
- ProgressBar.cs
- XmlLoader.cs