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;
}
}
}
}