using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Security; using System.Threading; namespace Vanara.PInvoke { /// Helper methods to work with asynchronous methods using . public static class OverlappedAsync { private static System.Collections.Generic.HashSet boundHandles = new System.Collections.Generic.HashSet(); /// Cleans up at the end of the callback method. /// The asynchronous result. /// The object passed into the method. /// asyncResult /// Argument must be of type AsyncResult - asyncResult /// Asynchronous end method called twice. /// Another Win32 error. public static object EndOverlappedFunction(IAsyncResult asyncResult) { if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); if (!(asyncResult is OverlappedAsyncResult result)) throw new ArgumentException("Argument must be of type AsyncResult", nameof(asyncResult)); if (1 == Interlocked.CompareExchange(ref result.endCalled, 1, 0)) throw new InvalidOperationException("Asynchronous end method called twice."); var handle = result.AsyncWaitHandle; if (!handle.SafeWaitHandle.IsClosed) try { handle.WaitOne(); } finally { handle.Close(); } if (result.errorCode != 0) throw new Win32Exception(result.errorCode); return result.AsyncState; } /// Cleans up and handles end of 'Begin' method and handles immediate return values. /// The asynchronous result. /// if set to true sets the return value as Complete. /// A completed or errored for the function /// /// Thrown when is false and the system is reporting a Win32 error. /// public static IAsyncResult EvaluateOverlappedFunction(OverlappedAsyncResult asyncResult, bool functionResult) { if (functionResult) { asyncResult.Complete(); return asyncResult; } var errorCode = Marshal.GetLastWin32Error(); if (errorCode == Win32Error.ERROR_IO_PENDING) return asyncResult; asyncResult.FreeOverlapped(); throw new Win32Exception(errorCode); } /// Setups the class that holds the overlapped call details. Use at the start of the 'Begin' method. /// The handle to bind to. /// The user callback method. /// /// A variable that is passed to all receiving methods in the asynchronous methods. Can be use to hold any user defined object. /// /// An instance for the asynchronous calls. public static unsafe OverlappedAsyncResult SetupOverlappedFunction(HFILE hDevice, AsyncCallback userCallback, object userState) { BindHandle(hDevice); var ar = new OverlappedAsyncResult(userState, userCallback, hDevice); var o = new Overlapped(0, 0, IntPtr.Zero, ar); ar.Overlapped = o.Pack((code, bytes, pOverlapped) => { var asyncResult = (OverlappedAsyncResult)Overlapped.Unpack(pOverlapped).AsyncResult; if (asyncResult.IsCompleted) return; asyncResult.FreeOverlapped(); if (code == 0x217) code = 0; asyncResult.Complete(true, (int)code); }, userState); return ar; } /// Setups the class that holds the overlapped call details. Use at the start of the 'Begin' method. /// The file to bind to. /// The user callback method. /// /// A variable that is passed to all receiving methods in the asynchronous methods. Can be use to hold any user defined object. /// /// An instance for the asynchronous calls. public static unsafe OverlappedAsyncResult SetupOverlappedFunction(System.IO.FileStream fileStream, AsyncCallback userCallback, object userState) { var ar = new OverlappedAsyncResult(userState, userCallback, fileStream.SafeFileHandle.DangerousGetHandle()); var o = new Overlapped(0, 0, IntPtr.Zero, ar); ar.Overlapped = o.Pack((code, bytes, pOverlapped) => { var asyncResult = (OverlappedAsyncResult)Overlapped.Unpack(pOverlapped).AsyncResult; if (asyncResult.IsCompleted) return; asyncResult.FreeOverlapped(); if (code == 0x217) code = 0; asyncResult.Complete(true, (int)code); }, userState); return ar; } private static void BindHandle(HFILE hDevice) { if (boundHandles.Add(hDevice)) ThreadPool.BindHandle(new Microsoft.Win32.SafeHandles.SafeFileHandle((IntPtr)hDevice, false)); } /// Holds all pertinent information for handling results and errors in an overlapped set of method calls. /// public sealed class OverlappedAsyncResult : IAsyncResult { internal int endCalled; internal int errorCode; [SecurityCritical] private readonly HFILE handle; private readonly object lockObj = new object(); private ManualResetEvent evt; [SecurityCritical] private unsafe NativeOverlapped* overlapped; /// Initializes a new instance of the class. /// State of the user. /// The callback method. /// The binding handle. internal OverlappedAsyncResult(object userState, AsyncCallback callback, HFILE hFile) { AsyncState = userState; AsyncCallback = callback; handle = hFile; } /// Gets the asynchronous callback method. /// The asynchronous callback method. public AsyncCallback AsyncCallback { get; } /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. public object AsyncState { get; } /// Gets a that is used to wait for an asynchronous operation to complete. public WaitHandle AsyncWaitHandle { get { lock (lockObj) { if (evt != null) return evt; evt = new ManualResetEvent(false); if (IsCompleted) evt.Set(); return evt; } } } /// Gets a value that indicates whether the asynchronous operation completed synchronously. public bool CompletedSynchronously { get; internal set; } = true; /// Gets the handle to which this operation is bound. /// The handle. public HFILE Handle => handle; /// Gets a value that indicates whether the asynchronous operation has completed. public bool IsCompleted { get; private set; } /// Gets the pointer. /// The pointer. public unsafe NativeOverlapped* Overlapped { get => overlapped; internal set => overlapped = value; } /// Completes the specified synch. /// The value for the property. /// The error code, if necessary. internal void Complete(bool synch = false, int error = 0) { CompletedSynchronously = synch; errorCode = error; if (IsCompleted) return; IsCompleted = true; lock (lockObj) { evt?.Set(); AsyncCallback?.Invoke(this); } } /// Frees the pointer. internal unsafe void FreeOverlapped() { System.Threading.Overlapped.Free(overlapped); overlapped = null; } } } }