private unsafe void Monitor(AsyncReadState state)
{
// This method should only ever access the directory handle via the state object passed in, and not access it
// via _directoryHandle. While this function is executing asynchronously, another thread could set
// EnableRaisingEvents to false and then back to true, restarting the FSW and causing a new directory handle
// and thread pool binding to be stored. This function could then get into an inconsistent state by doing some
// operations against the old handles and some against the new.
NativeOverlapped* overlappedPointer = null;
bool continueExecuting = false;
try
{
// If shutdown has been requested, exit. The finally block will handle
// cleaning up the entire operation, as continueExecuting will remain false.
if (!_enabled || IsHandleInvalid(state.DirectoryHandle))
return;
// Get the overlapped pointer to use for this iteration.
overlappedPointer = state.ThreadPoolBinding.AllocateNativeOverlapped(state.PreAllocatedOverlapped);
int size;
continueExecuting = Interop.Kernel32.ReadDirectoryChangesW(
state.DirectoryHandle,
state.Buffer, // the buffer is kept pinned for the duration of the sync and async operation by the PreAllocatedOverlapped
_internalBufferSize,
_includeSubdirectories,
(int)_notifyFilters,
out size,
overlappedPointer,
IntPtr.Zero);
}
catch (ObjectDisposedException)
{
// Ignore. Disposing of the handle is the mechanism by which the FSW communicates
// to the asynchronous operation to stop processing.
}
catch (ArgumentNullException)
{
//Ignore. The disposed handle could also manifest as an ArgumentNullException.
Debug.Assert(IsHandleInvalid(state.DirectoryHandle), "ArgumentNullException from something other than SafeHandle?");
}
finally
{
// At this point the operation has either been initiated and we'll let the callback
// handle things from here, or the operation has been stopped or failed, in which case
// we need to cleanup because we're no longer executing.
if (!continueExecuting)
{
// Clean up the overlapped pointer created for this iteration
if (overlappedPointer != null)
{
state.ThreadPoolBinding.FreeNativeOverlapped(overlappedPointer);
}
// Clean up the thread pool binding created for the entire operation
state.PreAllocatedOverlapped.Dispose();
state.ThreadPoolBinding.Dispose();
// Finally, if the handle was for some reason changed or closed during this call,
// then don't throw an exception. Otherwise, it's a valid error.
if (!IsHandleInvalid(state.DirectoryHandle))
{
OnError(new ErrorEventArgs(new Win32Exception()));
}
}
}
}