/// <summary>Start monitoring the current directory.</summary>
        private void StartRaisingEvents()
        {
            // If we're already running, don't do anything.
            if (!IsHandleInvalid(_directoryHandle))
            {
                return;
            }

            // Create handle to directory being monitored
            var defaultSecAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);

            _directoryHandle = Interop.mincore.CreateFile(
                lpFileName: _directory,
                dwDesiredAccess: Interop.mincore.FileOperations.FILE_LIST_DIRECTORY,
                dwShareMode: FileShare.Read | FileShare.Delete | FileShare.Write,
                securityAttrs: ref defaultSecAttrs,
                dwCreationDisposition: FileMode.Open,
                dwFlagsAndAttributes: Interop.mincore.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | Interop.mincore.FileOperations.FILE_FLAG_OVERLAPPED,
                hTemplateFile: IntPtr.Zero);
            if (IsHandleInvalid(_directoryHandle))
            {
                _directoryHandle = null;
                throw new FileNotFoundException(SR.Format(SR.FSW_IOError, _directory));
            }

            // Create the state associated with the operation of monitoring the direction
            AsyncReadState state;

            try
            {
                // Start ignoring all events that were initiated before this, and
                // allocate the buffer to be pinned and used for the duration of the operation
                int    session = Interlocked.Increment(ref _currentSession);
                byte[] buffer  = AllocateBuffer();

                // Store all state, including a preallocated overlapped, into the state object that'll be
                // passed from iteration to iteration during the lifetime of the operation.  The buffer will be pinned
                // from now until the end of the operation.
                state = new AsyncReadState(session, buffer, _directoryHandle, ThreadPoolBoundHandle.BindHandle(_directoryHandle));
                unsafe { state.PreAllocatedOverlapped = new PreAllocatedOverlapped(ReadDirectoryChangesCallback, state, buffer); }
            }
            catch
            {
                // Make sure we don't leave a valid directory handle set if we're not running
                _directoryHandle.Dispose();
                _directoryHandle = null;
                throw;
            }

            // Start monitoring
            _enabled = true;
            Monitor(state);
        }
예제 #2
0
    [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
    public void BindHandle_ClosedSyncHandleAsHandle_ThrowsArgumentException()
    {
        using (Win32Handle handle = HandleFactory.CreateSyncFileHandleForWrite(GetTestFilePath()))
        {
            handle.CloseWithoutDisposing();

            AssertExtensions.Throws <ArgumentException>("handle", () =>
            {
                ThreadPoolBoundHandle.BindHandle(handle);
            });
        }
    }
예제 #3
0
    [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
    public unsafe void FlowsAsyncLocalsToCallback_PreAllocatedOverlapped(bool shouldFlow)
    {
        // Makes sure that we flow async locals to callback

        const int DATA_SIZE = 2;

        SafeHandle            handle      = HandleFactory.CreateAsyncFileHandleForWrite(Path.Combine(TestDirectory, @"AsyncLocal.tmp"));
        ThreadPoolBoundHandle boundHandle = ThreadPoolBoundHandle.BindHandle(handle);

        OverlappedContext context = new OverlappedContext();

        byte[] data = new byte[DATA_SIZE];

        AsyncLocal <int> asyncLocal = new AsyncLocal <int>();

        asyncLocal.Value = 10;

        int?result = null;
        IOCompletionCallback callback = (_, __, ___) => {
            result = asyncLocal.Value;
            OnOverlappedOperationCompleted(_, __, ___);
        };

        using (PreAllocatedOverlapped preAlloc = shouldFlow ?
                                                 new PreAllocatedOverlapped(callback, context, data) :
                                                 PreAllocatedOverlapped.UnsafeCreate(callback, context, data))
        {
            NativeOverlapped *overlapped = boundHandle.AllocateNativeOverlapped(preAlloc);

            fixed(byte *p = data)
            {
                int retval = DllImport.WriteFile(boundHandle.Handle, p, DATA_SIZE, IntPtr.Zero, overlapped);

                if (retval == 0)
                {
                    Assert.Equal(DllImport.ERROR_IO_PENDING, Marshal.GetLastPInvokeError());
                }

                // Wait for overlapped operation to complete
                context.Event.WaitOne();
            }

            boundHandle.FreeNativeOverlapped(overlapped);
        }

        boundHandle.Dispose();
        handle.Dispose();

        Assert.Equal(
            shouldFlow ? 10 : 0,
            result);
    }
    private ThreadPoolBoundHandle CreateThreadPoolBoundHandle(SafeHandle handle, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0)
    {
        if (handle == null)
        {
            handle = HandleFactory.CreateAsyncFileHandleForWrite(GetTestFilePath(null, memberName, lineNumber));

            // ThreadPoolBoundHandle does not take ownership of the handle;
            // we must close it
            _handlesToDispose.Add(handle);
        }

        return(ThreadPoolBoundHandle.BindHandle(handle));
    }
    private ThreadPoolBoundHandle CreateThreadPoolBoundHandle(SafeHandle handle, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0)
    {
        handle = handle ?? HandleFactory.CreateAsyncFileHandleForWrite(GetTestFilePath(null, memberName, lineNumber));

        try
        {
            return(ThreadPoolBoundHandle.BindHandle(handle));
        }
        catch (ArgumentException ex) when(ex.GetType() == typeof(ArgumentException))
        {
            // TODO: Remove this try/catch, which is intended to help with debugging https://github.com/dotnet/corefx/issues/18058
            throw new ArgumentException("Handle value: " + handle.DangerousGetHandle(), ex.ParamName, ex);
        }
    }
    [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
    public void BindHandle_AlreadyBoundHandleAsHandle_ThrowsArgumentException()
    {
        using (SafeHandle handle = HandleFactory.CreateAsyncFileHandleForWrite())
        {
            // Once
            ThreadPoolBoundHandle.BindHandle(handle);

            AssertExtensions.Throws <ArgumentException>("handle", () =>
            {
                // Twice
                ThreadPoolBoundHandle.BindHandle(handle);
            });
        }
    }
    [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
    public unsafe void GetNativeOverlappedState_WhenUnderlyingStateIsNull_ReturnsNull()
    {
        using (SafeHandle handle = HandleFactory.CreateAsyncFileHandleForWrite(GetTestFilePath()))
        {
            using (ThreadPoolBoundHandle boundHandle = ThreadPoolBoundHandle.BindHandle(handle))
            {
                NativeOverlapped *overlapped = boundHandle.AllocateNativeOverlapped((_, __, ___) => { }, (object)null, new byte[0]);

                object result = ThreadPoolBoundHandle.GetNativeOverlappedState(overlapped);

                Assert.Null(result);

                boundHandle.FreeNativeOverlapped(overlapped);
            }
        }
    }
예제 #8
0
        // Win32 file channel impl
        // TODO: Other platforms
        public unsafe void OpenReadFile(string path)
        {
            var fileHandle = CreateFile(path, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, EFileAttributes.Overlapped, IntPtr.Zero);

            var handle = ThreadPoolBoundHandle.BindHandle(fileHandle);

            var readOperation = new ReadOperation
            {
                Channel    = _channel,
                FileHandle = fileHandle,
                Handle     = handle,
                IOCallback = IOCallback
            };

            _channel.OnStartReading(readOperation.Read);
            _channel.OnDispose(fileHandle.Dispose);
        }
예제 #9
0
        internal RequestQueue(UrlGroup urlGroup)
        {
            _urlGroup = urlGroup;

            var queueName = "IISConsole";
            HttpRequestQueueV2Handle requestQueueHandle = null;

            var statusCode = HttpApi.HttpCreateRequestQueue(
                HttpApi.Version, queueName, null, HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAGS.HTTP_CREATE_REQUEST_QUEUE_FLAG_CONTROLLER, out requestQueueHandle);

            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
            {
                throw new HttpSysException((int)statusCode);
            }

            Handle      = requestQueueHandle;
            BoundHandle = ThreadPoolBoundHandle.BindHandle(Handle);
        }
    public unsafe void GetNativeOverlappedState_WhenUnderlyingStateIsObject_ReturnsObject()
    {
        object context = new object();

        using (SafeHandle handle = HandleFactory.CreateAsyncFileHandleForWrite())
        {
            using (ThreadPoolBoundHandle boundHandle = ThreadPoolBoundHandle.BindHandle(handle))
            {
                NativeOverlapped *overlapped = boundHandle.AllocateNativeOverlapped((_, __, ___) => { }, context, new byte[0]);

                object result = ThreadPoolBoundHandle.GetNativeOverlappedState(overlapped);

                Assert.Same(context, result);

                boundHandle.FreeNativeOverlapped(overlapped);
            }
        }
    }
예제 #11
0
        // Binds the Socket Win32 Handle to the ThreadPool's CompletionPort.
        public ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle()
        {
            if (_released)
            {
                // Keep the exception message pointing at the external type.
                throw new ObjectDisposedException(typeof(Socket).FullName);
            }

            // Check to see if the socket native _handle is already
            // bound to the ThreadPool's completion port.
            if (_iocpBoundHandle == null)
            {
                lock (_iocpBindingLock)
                {
                    if (_iocpBoundHandle == null)
                    {
                        // Bind the socket native _handle to the ThreadPool.
                        if (GlobalLog.IsEnabled)
                        {
                            GlobalLog.Print("SafeCloseSocket#" + LoggingHash.HashString(this) + "::BindToCompletionPort() calling ThreadPool.BindHandle()");
                        }

                        try
                        {
                            // The handle (this) may have been already released:
                            // E.g.: The socket has been disposed in the main thread. A completion callback may
                            //       attempt starting another operation.
                            _iocpBoundHandle = ThreadPoolBoundHandle.BindHandle(this);
                        }
                        catch (Exception exception)
                        {
                            if (ExceptionCheck.IsFatal(exception))
                            {
                                throw;
                            }
                            CloseAsIs();
                            throw;
                        }
                    }
                }
            }

            return(_iocpBoundHandle);
        }
    [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
    public unsafe void GetNativeOverlappedState_WhenUnderlyingStateIsIAsyncResult_ReturnsIAsyncResult()
    {                                         // CoreCLR/Desktop CLR version of overlapped sits on top of Overlapped class
        // and treats IAsyncResult specially, which is why we special case this case.

        AsyncResult context = new AsyncResult();

        using (SafeHandle handle = HandleFactory.CreateAsyncFileHandleForWrite(GetTestFilePath()))
        {
            using (ThreadPoolBoundHandle boundHandle = ThreadPoolBoundHandle.BindHandle(handle))
            {
                NativeOverlapped *overlapped = boundHandle.AllocateNativeOverlapped((_, __, ___) => { }, context, new byte[0]);

                object result = ThreadPoolBoundHandle.GetNativeOverlappedState(overlapped);

                Assert.Same(context, result);

                boundHandle.FreeNativeOverlapped(overlapped);
            }
        }
    }
예제 #13
0
        private void StartRaisingEvents()
        {
            // 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.Format(SR.FSW_IOError, _directory));
            }

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

            // Attach handle to thread pool

            _threadPoolBinding = ThreadPoolBoundHandle.BindHandle(_directoryHandle);
            _enabled           = true;

            // Setup IO completion port
            Monitor(null);
        }
예제 #14
0
        // Win32 file impl
        // TODO: Other platforms
        public unsafe void OpenReadFile(string path)
        {
            var fileHandle = CreateFile(path, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, EFileAttributes.Overlapped, IntPtr.Zero);

            var handle = ThreadPoolBoundHandle.BindHandle(fileHandle);

            var readOperation = new ReadOperation
            {
                Writer                = _writer,
                FileHandle            = fileHandle,
                ThreadPoolBoundHandle = handle,
                IOCallback            = IOCallback
            };

            var overlapped = new PreAllocatedOverlapped(IOCallback, readOperation, null);

            readOperation.PreAllocatedOverlapped = overlapped;

            Task.Factory.StartNew(state =>
            {
                ((ReadOperation)state).Read();
            },
                                  readOperation);
        }
예제 #15
0
        // Win32 file channel impl
        // TODO: Other platforms
        public unsafe void OpenReadFile(string path)
        {
            var fileHandle = CreateFile(path, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, EFileAttributes.Overlapped, IntPtr.Zero);

            var handle = ThreadPoolBoundHandle.BindHandle(fileHandle);

            var readOperation = new ReadOperation
            {
                Channel               = _channel,
                FileHandle            = fileHandle,
                ThreadPoolBoundHandle = handle,
                IOCallback            = IOCallback
            };

            var overlapped = new PreAllocatedOverlapped(IOCallback, readOperation, null);

            readOperation.PreAllocatedOverlapped = overlapped;

            _channel.ReadingStarted.ContinueWith((t, state) =>
            {
                ((ReadOperation)state).Read();
            },
                                                 readOperation);
        }
예제 #16
0
    private static ThreadPoolBoundHandle CreateThreadPoolBoundHandle(SafeHandle handle)
    {
        handle = handle ?? HandleFactory.CreateAsyncFileHandleForWrite();

        return(ThreadPoolBoundHandle.BindHandle(handle));
    }
예제 #17
0
 /// <summary>Initializes the handle to be used asynchronously.</summary>
 /// <param name="handle">The handle.</param>
 private void InitializeAsyncHandle(SafePipeHandle handle)
 {
     // If the handle is of async type, bind the handle to the ThreadPool so that we can use
     // the async operations (it's needed so that our native callbacks get called).
     _threadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle);
 }
예제 #18
0
    private ThreadPoolBoundHandle CreateThreadPoolBoundHandle(SafeHandle handle, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0)
    {
        handle = handle ?? HandleFactory.CreateAsyncFileHandleForWrite(GetTestFilePath(null, memberName, lineNumber));

        return(ThreadPoolBoundHandle.BindHandle(handle));
    }
    [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
    public unsafe void MultipleOperationsOverMultipleHandles()
    {
        const int DATA_SIZE = 2;

        SafeHandle            handle1      = HandleFactory.CreateAsyncFileHandleForWrite(Path.Combine(TestDirectory, @"MultipleOperationsOverMultipleHandle1.tmp"));
        SafeHandle            handle2      = HandleFactory.CreateAsyncFileHandleForWrite(Path.Combine(TestDirectory, @"MultipleOperationsOverMultipleHandle2.tmp"));
        ThreadPoolBoundHandle boundHandle1 = ThreadPoolBoundHandle.BindHandle(handle1);
        ThreadPoolBoundHandle boundHandle2 = ThreadPoolBoundHandle.BindHandle(handle2);

        OverlappedContext result1 = new OverlappedContext();
        OverlappedContext result2 = new OverlappedContext();

        byte[] data1 = new byte[DATA_SIZE];
        data1[0] = (byte)'A';
        data1[1] = (byte)'B';


        byte[] data2 = new byte[DATA_SIZE];
        data2[0] = (byte)'C';
        data2[1] = (byte)'D';

        PreAllocatedOverlapped preAlloc1 = new PreAllocatedOverlapped(OnOverlappedOperationCompleted, result1, data1);
        PreAllocatedOverlapped preAlloc2 = new PreAllocatedOverlapped(OnOverlappedOperationCompleted, result2, data2);

        for (int i = 0; i < 10; i++)
        {
            NativeOverlapped *overlapped1 = boundHandle1.AllocateNativeOverlapped(preAlloc1);
            NativeOverlapped *overlapped2 = boundHandle2.AllocateNativeOverlapped(preAlloc2);

            fixed(byte *p1 = data1, p2 = data2)
            {
                int retval = DllImport.WriteFile(boundHandle1.Handle, p1, DATA_SIZE, IntPtr.Zero, overlapped1);

                if (retval == 0)
                {
                    Assert.Equal(DllImport.ERROR_IO_PENDING, Marshal.GetLastWin32Error());
                }


                retval = DllImport.WriteFile(boundHandle2.Handle, p2, DATA_SIZE, IntPtr.Zero, overlapped2);

                if (retval == 0)
                {
                    Assert.Equal(DllImport.ERROR_IO_PENDING, Marshal.GetLastWin32Error());
                }

                // Wait for overlapped operations to complete
                WaitHandle.WaitAll(new WaitHandle[] { result1.Event, result2.Event });
            }

            boundHandle1.FreeNativeOverlapped(overlapped1);
            boundHandle2.FreeNativeOverlapped(overlapped2);

            result1.Event.Reset();
            result2.Event.Reset();

            Assert.Equal(0, result1.ErrorCode);
            Assert.Equal(0, result2.ErrorCode);
            Assert.Equal(DATA_SIZE, result1.BytesWritten);
            Assert.Equal(DATA_SIZE, result2.BytesWritten);
        }

        boundHandle1.Dispose();
        boundHandle2.Dispose();
        preAlloc1.Dispose();
        preAlloc2.Dispose();
        handle1.Dispose();
        handle2.Dispose();
    }
예제 #20
0
    private RequestQueue(UrlGroup?urlGroup, string?requestQueueName, RequestQueueMode mode, ILogger logger, bool receiver)
    {
        _mode    = mode;
        UrlGroup = urlGroup;
        _logger  = logger;

        var flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.None;

        Created = true;

        if (_mode == RequestQueueMode.Attach)
        {
            flags   = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting;
            Created = false;
            if (receiver)
            {
                flags |= HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.Delegation;
            }
        }

        var statusCode = HttpApi.HttpCreateRequestQueue(
            HttpApi.Version,
            requestQueueName,
            null,
            flags,
            out var requestQueueHandle);

        if (_mode == RequestQueueMode.CreateOrAttach && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS)
        {
            // Tried to create, but it already exists so attach to it instead.
            Created    = false;
            flags      = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting;
            statusCode = HttpApi.HttpCreateRequestQueue(
                HttpApi.Version,
                requestQueueName,
                null,
                flags,
                out requestQueueHandle);
        }

        if (flags.HasFlag(HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting) && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_FILE_NOT_FOUND)
        {
            throw new HttpSysException((int)statusCode, $"Failed to attach to the given request queue '{requestQueueName}', the queue could not be found.");
        }
        else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_NAME)
        {
            throw new HttpSysException((int)statusCode, $"The given request queue name '{requestQueueName}' is invalid.");
        }
        else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
        {
            throw new HttpSysException((int)statusCode);
        }

        // Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS)
        if (HttpSysListener.SkipIOCPCallbackOnSuccess &&
            !UnsafeNclNativeMethods.SetFileCompletionNotificationModes(
                requestQueueHandle,
                UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipCompletionPortOnSuccess |
                UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipSetEventOnHandle))
        {
            requestQueueHandle.Dispose();
            throw new HttpSysException(Marshal.GetLastWin32Error());
        }

        Handle      = requestQueueHandle;
        BoundHandle = ThreadPoolBoundHandle.BindHandle(Handle);

        if (!Created)
        {
            Log.AttachedToQueue(_logger, requestQueueName);
        }
    }
 public void BindHandle_ValidHandle_ThrowsPlatformNotSupportedException()
 {
     Assert.Throws <PlatformNotSupportedException>(() => ThreadPoolBoundHandle.BindHandle(new Win32Handle(new IntPtr(1))));
 }
예제 #22
0
        /// <summary>Start monitoring the current directory.</summary>
        private void StartRaisingEvents()
        {
            // If we're called when "Initializing" is true, set enabled to true
            if (IsSuspended())
            {
                _enabled = true;
                return;
            }

            // If we're already running, don't do anything.
            if (!IsHandleInvalid(_directoryHandle))
            {
                return;
            }

            // Create handle to directory being monitored
            _directoryHandle = Interop.Kernel32.CreateFile(
                lpFileName: _directory,
                dwDesiredAccess: Interop.Kernel32.FileOperations.FILE_LIST_DIRECTORY,
                dwShareMode: FileShare.Read | FileShare.Delete | FileShare.Write,
                dwCreationDisposition: FileMode.Open,
                dwFlagsAndAttributes: Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | Interop.Kernel32.FileOperations.FILE_FLAG_OVERLAPPED);

            if (IsHandleInvalid(_directoryHandle))
            {
                _directoryHandle = null;
                throw new FileNotFoundException(SR.Format(SR.FSW_IOError, _directory));
            }

            // Create the state associated with the operation of monitoring the direction
            AsyncReadState state;

            try
            {
                // Start ignoring all events that were initiated before this, and
                // allocate the buffer to be pinned and used for the duration of the operation
                int    session = Interlocked.Increment(ref _currentSession);
                byte[] buffer  = AllocateBuffer();

                // Store all state, including a preallocated overlapped, into the state object that'll be
                // passed from iteration to iteration during the lifetime of the operation.  The buffer will be pinned
                // from now until the end of the operation.
                state = new AsyncReadState(session, buffer, _directoryHandle, ThreadPoolBoundHandle.BindHandle(_directoryHandle), this);
                unsafe
                {
                    state.PreAllocatedOverlapped = new PreAllocatedOverlapped((errorCode, numBytes, overlappedPointer) =>
                    {
                        AsyncReadState state = (AsyncReadState)ThreadPoolBoundHandle.GetNativeOverlappedState(overlappedPointer) !;
                        state.ThreadPoolBinding.FreeNativeOverlapped(overlappedPointer);
                        if (state.WeakWatcher.TryGetTarget(out FileSystemWatcher? watcher))
                        {
                            watcher.ReadDirectoryChangesCallback(errorCode, numBytes, state);
                        }
                    }, state, buffer);
                }
            }
            catch
            {
                // Make sure we don't leave a valid directory handle set if we're not running
                _directoryHandle.Dispose();
                _directoryHandle = null;
                throw;
            }

            // Start monitoring
            _enabled = true;
            Monitor(state);
        }