public NTStatus NotifyChange(out object ioRequest, object handle, NotifyChangeFilter completionFilter, bool watchTree, int outputBufferSize, OnNotifyChangeCompleted onNotifyChangeCompleted, object context) { byte[] buffer = new byte[outputBufferSize]; ManualResetEvent requestAddedEvent = new ManualResetEvent(false); PendingRequest request = new PendingRequest(); Thread m_thread = new Thread(delegate() { request.FileHandle = (IntPtr)handle; request.ThreadID = ThreadingHelper.GetCurrentThreadId(); m_pendingRequests.Add(request); // The request has been added, we can now return STATUS_PENDING. requestAddedEvent.Set(); // There is a possibility of race condition if the caller will wait for STATUS_PENDING and then immediate call Cancel, but this scenario is very unlikely. NTStatus status = NtNotifyChangeDirectoryFile((IntPtr)handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out request.IOStatusBlock, buffer, (uint)buffer.Length, completionFilter, watchTree); if (status == NTStatus.STATUS_SUCCESS) { int length = (int)request.IOStatusBlock.Information; buffer = ByteReader.ReadBytes(buffer, 0, length); } else { const NTStatus STATUS_ALERTED = (NTStatus)0x00000101; const NTStatus STATUS_OBJECT_TYPE_MISMATCH = (NTStatus)0xC0000024; buffer = new byte[0]; if (status == STATUS_OBJECT_TYPE_MISMATCH) { status = NTStatus.STATUS_INVALID_HANDLE; } else if (status == STATUS_ALERTED) { status = NTStatus.STATUS_CANCELLED; } // If the handle is closing and we had to cancel a ChangeNotify request as part of a cleanup, // we return STATUS_NOTIFY_CLEANUP as specified in [MS-FSA] 2.1.5.4. if (status == NTStatus.STATUS_CANCELLED && request.Cleanup) { status = NTStatus.STATUS_NOTIFY_CLEANUP; } } onNotifyChangeCompleted(status, buffer, context); m_pendingRequests.Remove((IntPtr)handle, request.ThreadID); }); m_thread.Start(); // We must wait for the request to be added in order for Cancel to function properly. requestAddedEvent.WaitOne(); ioRequest = request; return(NTStatus.STATUS_PENDING); }
public void Add(PendingRequest request) { lock (m_handleToNotifyChangeRequests) { List <PendingRequest> pendingRequests; bool containsKey = m_handleToNotifyChangeRequests.TryGetValue(request.FileHandle, out pendingRequests); if (containsKey) { pendingRequests.Add(request); } else { pendingRequests = new List <PendingRequest>(); pendingRequests.Add(request); m_handleToNotifyChangeRequests.Add(request.FileHandle, pendingRequests); } } }
public NTStatus Cancel(object ioRequest) { PendingRequest request = (PendingRequest)ioRequest; const uint THREAD_TERMINATE = 0x00000001; const uint THREAD_ALERT = 0x00000004; uint threadID = request.ThreadID; IntPtr threadHandle = ThreadingHelper.OpenThread(THREAD_TERMINATE | THREAD_ALERT, false, threadID); if (threadHandle == IntPtr.Zero) { Win32Error error = (Win32Error)Marshal.GetLastWin32Error(); if (error == Win32Error.ERROR_INVALID_PARAMETER) { return(NTStatus.STATUS_INVALID_HANDLE); } else { throw new Exception("OpenThread failed, Win32 error: " + error.ToString("D")); } } NTStatus status; if (System.Environment.OSVersion.Version.Major >= 6) { IO_STATUS_BLOCK ioStatusBlock; status = NtCancelSynchronousIoFile(threadHandle, IntPtr.Zero, out ioStatusBlock); } else { // The handle was opened for synchronous operation so NtNotifyChangeDirectoryFile is blocking. // We MUST use NtAlertThread to send a signal to stop the wait. The handle cannot be closed otherwise. // Note: The handle was opened with CreateOptions.FILE_SYNCHRONOUS_IO_ALERT as required. status = NtAlertThread(threadHandle); } ThreadingHelper.CloseHandle(threadHandle); m_pendingRequests.Remove(request.FileHandle, request.ThreadID); return(status); }