/// <devdoc> /// Callback from thread pool. /// </devdoc> /// <internalonly/> private unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped *overlappedPointer) { Overlapped overlapped = Overlapped.Unpack(overlappedPointer); 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; string name = null; do { fixed(byte *buffPtr = asyncResult.buffer) { // Get next offset: nextOffset = *((int *)(buffPtr + offset)); // Get change flag: action = *((int *)(buffPtr + offset + 4)); // Get filename length (in bytes): nameLength = *((int *)(buffPtr + offset + 8)); name = new String((char *)(buffPtr + offset + 12), 0, 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) { NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName); oldName = null; } // Notify each file of change NotifyFileSystemEventArgs(action, name); } offset += nextOffset; } while (nextOffset != 0); if (oldName != null) { Debug.Assert(false, "FileSystemWatcher: FILE_ACTION_RENAMED_OLD_NAME with no" + "new name! [" + oldName + "]"); NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName); oldName = null; } } } } finally { Overlapped.Free(overlappedPointer); if (!stopListening && !runOnce) { Monitor(asyncResult.buffer); } } }
private unsafe void Monitor(byte[] buffer) { if (!enabled || IsHandleInvalid) { return; } Overlapped overlapped = new Overlapped(); if (buffer == null) { try { buffer = new byte[internalBufferSize]; } catch (OutOfMemoryException) { throw new OutOfMemoryException(SR.GetString(SR.BufferSizeTooLarge, internalBufferSize.ToString(CultureInfo.CurrentCulture))); } } // Pass "session" counter to callback: FSWAsyncResult asyncResult = new FSWAsyncResult(); asyncResult.session = currentSession; asyncResult.buffer = buffer; // Pack overlapped. The buffer will be pinned by Overlapped: overlapped.AsyncResult = asyncResult; NativeOverlapped *overlappedPointer = overlapped.Pack(new IOCompletionCallback(this.CompletionStatusChanged), buffer); // Can now call OS: 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 fixed(byte *buffPtr = buffer) { ok = UnsafeNativeMethods.ReadDirectoryChangesW(directoryHandle, new HandleRef(this, (IntPtr)buffPtr), internalBufferSize, includeSubdirectories ? 1 : 0, (int)notifyFilters, out size, overlappedPointer, NativeMethods.NullHandleRef); } } } catch (ObjectDisposedException) { //Ignore Debug.Assert(IsHandleInvalid, "ObjectDisposedException from something other than SafeHandle?"); } catch (ArgumentNullException) { //Ignore Debug.Assert(IsHandleInvalid, "ArgumentNullException from something other than SafeHandle?"); } finally { if (!ok) { Overlapped.Free(overlappedPointer); // If the handle was for some reason changed or closed during this call, then don't throw an // exception. Else, it's a valid error. if (!IsHandleInvalid) { OnError(new ErrorEventArgs(new Win32Exception())); } } } }
private unsafe void Monitor(byte[] buffer) { if (!enabled || IsHandleInvalid) { return; } Overlapped overlapped = new Overlapped(); if (buffer == null) { try { buffer = new byte[internalBufferSize]; } catch (OutOfMemoryException) { throw new OutOfMemoryException(SR.GetString(SR.BufferSizeTooLarge, internalBufferSize.ToString(CultureInfo.CurrentCulture))); } } // Pass "session" counter to callback: FSWAsyncResult asyncResult = new FSWAsyncResult(); asyncResult.session = currentSession; asyncResult.buffer = buffer; // Pack overlapped. The buffer will be pinned by Overlapped: overlapped.AsyncResult = asyncResult; NativeOverlapped* overlappedPointer = overlapped.Pack(new IOCompletionCallback(this.CompletionStatusChanged), buffer); // Can now call OS: 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 fixed (byte * buffPtr = buffer) { ok = UnsafeNativeMethods.ReadDirectoryChangesW(directoryHandle, new HandleRef(this, (IntPtr) buffPtr), internalBufferSize, includeSubdirectories ? 1 : 0, (int) notifyFilters, out size, overlappedPointer, NativeMethods.NullHandleRef); } } } catch (ObjectDisposedException ) { //Ignore Debug.Assert(IsHandleInvalid, "ObjectDisposedException from something other than SafeHandle?"); } catch (ArgumentNullException ) { //Ignore Debug.Assert(IsHandleInvalid, "ArgumentNullException from something other than SafeHandle?"); } finally { if (! ok) { Overlapped.Free(overlappedPointer); // If the handle was for some reason changed or closed during this call, then don't throw an // exception. Else, it's a valid error. if (!IsHandleInvalid) { OnError(new ErrorEventArgs(new Win32Exception())); } } } }
/// <devdoc> /// Calls native API and sets up handle with the directory change API. /// </devdoc> /// <internalonly/> private unsafe void Monitor(byte[] buffer) { if (!_enabled || IsHandleInvalid) { return; } if (buffer == null) { buffer = AllocateBuffer(); } // Pass "session" counter to callback: FSWAsyncResult asyncResult = new FSWAsyncResult(); asyncResult.session = _currentSession; asyncResult.buffer = buffer; NativeOverlapped* overlappedPointer = _threadPoolBinding.AllocateNativeOverlapped(new IOCompletionCallback(this.CompletionStatusChanged), asyncResult, buffer); // Can now call OS: 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 fixed (byte* buffPtr = buffer) { ok = UnsafeNativeMethods.ReadDirectoryChangesW(_directoryHandle, buffPtr, _internalBufferSize, _includeSubdirectories ? 1 : 0, (int)_notifyFilters, out size, overlappedPointer, IntPtr.Zero); } } } catch (ObjectDisposedException) { //Ignore } catch (ArgumentNullException) { //Ignore Debug.Assert(IsHandleInvalid, "ArgumentNullException from something other than SafeHandle?"); } finally { if (!ok) { _threadPoolBinding.FreeNativeOverlapped(overlappedPointer); // If the handle was for some reason changed or closed during this call, then don't throw an // exception. Else, it's a valid error. if (!IsHandleInvalid) { OnError(new ErrorEventArgs(new Win32Exception())); } } } }
/// <devdoc> /// Calls native API and sets up handle with the directory change API. /// </devdoc> /// <internalonly/> private unsafe void Monitor(byte[] buffer) { if (!_enabled || IsHandleInvalid) { return; } if (buffer == null) { buffer = AllocateBuffer(); } // Pass "session" counter to callback: FSWAsyncResult asyncResult = new FSWAsyncResult(); asyncResult.session = _currentSession; asyncResult.buffer = buffer; NativeOverlapped *overlappedPointer = _threadPoolBinding.AllocateNativeOverlapped(new IOCompletionCallback(this.CompletionStatusChanged), asyncResult, buffer); // Can now call OS: 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 fixed(byte *buffPtr = buffer) { ok = UnsafeNativeMethods.ReadDirectoryChangesW(_directoryHandle, buffPtr, _internalBufferSize, _includeSubdirectories ? 1 : 0, (int)_notifyFilters, out size, overlappedPointer, IntPtr.Zero); } } } catch (ObjectDisposedException) { //Ignore } catch (ArgumentNullException) { //Ignore Debug.Assert(IsHandleInvalid, "ArgumentNullException from something other than SafeHandle?"); } finally { if (!ok) { _threadPoolBinding.FreeNativeOverlapped(overlappedPointer); // If the handle was for some reason changed or closed during this call, then don't throw an // exception. Else, it's a valid error. if (!IsHandleInvalid) { OnError(new ErrorEventArgs(new Win32Exception())); } } } }
private unsafe void Monitor(byte[] buffer) { if (this.enabled && !this.IsHandleInvalid) { Overlapped overlapped = new Overlapped(); if (buffer == null) { try { buffer = new byte[this.internalBufferSize]; } catch (OutOfMemoryException) { throw new OutOfMemoryException(SR.GetString("BufferSizeTooLarge", new object[] { this.internalBufferSize.ToString(CultureInfo.CurrentCulture) })); } } FSWAsyncResult result = new FSWAsyncResult { session = this.currentSession, buffer = buffer }; overlapped.AsyncResult = result; NativeOverlapped *overlappedPointer = overlapped.Pack(new IOCompletionCallback(this.CompletionStatusChanged), buffer); bool flag = false; try { if (!this.IsHandleInvalid) { try { byte[] buffer2; if (((buffer2 = buffer) == null) || (buffer2.Length == 0)) { numRef = null; goto Label_00B3; } fixed(byte *numRef = buffer2) { int num; Label_00B3: flag = Microsoft.Win32.UnsafeNativeMethods.ReadDirectoryChangesW(this.directoryHandle, new HandleRef(this, (IntPtr)numRef), this.internalBufferSize, this.includeSubdirectories ? 1 : 0, (int)this.notifyFilters, out num, overlappedPointer, Microsoft.Win32.NativeMethods.NullHandleRef); } } finally { numRef = null; } } } catch (ObjectDisposedException) { } catch (ArgumentNullException) { } finally { if (!flag) { Overlapped.Free(overlappedPointer); if (!this.IsHandleInvalid) { this.OnError(new ErrorEventArgs(new Win32Exception())); } } } } }
private unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped *overlappedPointer) { FSWAsyncResult asyncResult = (FSWAsyncResult)Overlapped.Unpack(overlappedPointer).AsyncResult; try { if (!this.stopListening) { lock (this) { if (errorCode != 0) { if (errorCode != 0x3e3) { this.OnError(new ErrorEventArgs(new Win32Exception((int)errorCode))); this.EnableRaisingEvents = false; } } else if (asyncResult.session == this.currentSession) { if (numBytes == 0) { this.NotifyInternalBufferOverflowEvent(); } else { int num2; int index = 0; string oldName = null; string name = null; do { int num3; try { fixed(byte *numRef = asyncResult.buffer) { num2 = numRef[index]; num3 = (numRef + index)[4]; int num4 = (numRef + index)[8]; name = new string((char *)((numRef + index) + 12), 0, num4 / 2); } } finally { numRef = null; } switch (num3) { case 4: oldName = name; break; case 5: if (oldName != null) { this.NotifyRenameEventArgs(WatcherChangeTypes.Renamed, name, oldName); oldName = null; } else { this.NotifyRenameEventArgs(WatcherChangeTypes.Renamed, name, oldName); oldName = null; } break; default: if (oldName != null) { this.NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName); oldName = null; } this.NotifyFileSystemEventArgs(num3, name); break; } index += num2; }while (num2 != 0); if (oldName != null) { this.NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName); oldName = null; } } } } } } finally { Overlapped.Free(overlappedPointer); if (!this.stopListening && !this.runOnce) { this.Monitor(asyncResult.buffer); } } }