#if !(NET20 || NET35)
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Vanara.PInvoke
{
public static partial class Kernel32
{
///
/// Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
///
///
/// A handle to the device on which the operation is to be performed. The device is typically a volume, directory, file, or stream.
/// To retrieve a device handle, use the CreateFile function. For more information, see Remarks.
///
///
/// The control code for the operation. This value identifies the specific operation to be performed and the type of device on which
/// to perform it.
///
/// The input buffer required to perform the operation. Can be null if unnecessary.
/// The output buffer that is to receive the data returned by the operation. Can be null if unnecessary.
/// An asynchronous empty result.
///
///
/// To retrieve a handle to the device, you must call the CreateFile function with either the name of a device or the name of the
/// driver associated with a device. To specify a device name, use the following format:
///
/// \\.\DeviceName
///
/// DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive
/// A: with CreateFile, specify \\.\a:. Alternatively, you can use the names \\.\PhysicalDrive0, \\.\PhysicalDrive1, and so on, to
/// open handles to the physical drives on a system.
///
///
/// You should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when calling CreateFile to open a handle to a device
/// driver. However, when you open a communications resource, such as a serial port, you must specify exclusive access. Use the other
/// CreateFile parameters as follows when opening a device handle:
///
///
/// -
/// The fdwCreate parameter must specify OPEN_EXISTING.
///
/// -
/// The hTemplateFile parameter must be NULL.
///
/// -
///
/// The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped
/// (asynchronous) I/O operations.
///
///
///
///
public static async Task DeviceIoControlAsync(HFILE hDev, uint ioControlCode, byte[] inputBuffer, byte[] outputBuffer)
{
var buf = Pack(inputBuffer, outputBuffer);
var outputBytes = await Task.Factory.FromAsync(BeginDeviceIoControl, EndDeviceIoControl, hDev, ioControlCode, buf, null);
outputBytes.CopyTo(outputBuffer, 0);
}
///
/// Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
///
/// The type of the .
///
/// A handle to the device on which the operation is to be performed. The device is typically a volume, directory, file, or stream.
/// To retrieve a device handle, use the CreateFile function. For more information, see Remarks.
///
///
/// The control code for the operation. This value identifies the specific operation to be performed and the type of device on which
/// to perform it.
///
///
/// The input value required to perform the operation. The type of this data depends on the value of the parameter.
///
/// An asynchronous empty result.
///
///
/// To retrieve a handle to the device, you must call the CreateFile function with either the name of a device or the name of the
/// driver associated with a device. To specify a device name, use the following format:
///
/// \\.\DeviceName
///
/// DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive
/// A: with CreateFile, specify \\.\a:. Alternatively, you can use the names \\.\PhysicalDrive0, \\.\PhysicalDrive1, and so on, to
/// open handles to the physical drives on a system.
///
///
/// You should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when calling CreateFile to open a handle to a device
/// driver. However, when you open a communications resource, such as a serial port, you must specify exclusive access. Use the other
/// CreateFile parameters as follows when opening a device handle:
///
///
/// -
/// The fdwCreate parameter must specify OPEN_EXISTING.
///
/// -
/// The hTemplateFile parameter must be NULL.
///
/// -
///
/// The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped
/// (asynchronous) I/O operations.
///
///
///
///
public static Task DeviceIoControlAsync(HFILE hDev, uint ioControlCode, TIn inVal) where TIn : struct =>
DeviceIoControlAsync(hDev, ioControlCode, inVal);
///
/// Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
///
/// The type of the return value.
///
/// A handle to the device on which the operation is to be performed. The device is typically a volume, directory, file, or stream.
/// To retrieve a device handle, use the CreateFile function. For more information, see Remarks.
///
///
/// The control code for the operation. This value identifies the specific operation to be performed and the type of device on which
/// to perform it.
///
/// An asynchronous result containing the resulting value of type .
///
///
/// To retrieve a handle to the device, you must call the CreateFile function with either the name of a device or the name of the
/// driver associated with a device. To specify a device name, use the following format:
///
/// \\.\DeviceName
///
/// DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive
/// A: with CreateFile, specify \\.\a:. Alternatively, you can use the names \\.\PhysicalDrive0, \\.\PhysicalDrive1, and so on, to
/// open handles to the physical drives on a system.
///
///
/// You should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when calling CreateFile to open a handle to a device
/// driver. However, when you open a communications resource, such as a serial port, you must specify exclusive access. Use the other
/// CreateFile parameters as follows when opening a device handle:
///
///
/// -
/// The fdwCreate parameter must specify OPEN_EXISTING.
///
/// -
/// The hTemplateFile parameter must be NULL.
///
/// -
///
/// The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped
/// (asynchronous) I/O operations.
///
///
///
///
public static Task DeviceIoControlAsync(HFILE hDev, uint ioControlCode) where TOut : struct => DeviceIoControlAsync(hDev, ioControlCode, null);
///
/// Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
///
/// The type of the .
/// The type of the .
///
/// A handle to the device on which the operation is to be performed. The device is typically a volume, directory, file, or stream.
/// To retrieve a device handle, use the CreateFile function. For more information, see Remarks.
///
///
/// The control code for the operation. This value identifies the specific operation to be performed and the type of device on which
/// to perform it.
///
///
/// The input value required to perform the operation. The type of this data depends on the value of the parameter.
///
///
/// The output value that is to receive the data returned by the operation. The type of this data depends on the value of the
/// dwIoControlCode parameter.
///
/// An asynchronous result containing the populated data supplied by .
///
///
/// To retrieve a handle to the device, you must call the CreateFile function with either the name of a device or the name of the
/// driver associated with a device. To specify a device name, use the following format:
///
/// \\.\DeviceName
///
/// DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive
/// A: with CreateFile, specify \\.\a:. Alternatively, you can use the names \\.\PhysicalDrive0, \\.\PhysicalDrive1, and so on, to
/// open handles to the physical drives on a system.
///
///
/// You should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when calling CreateFile to open a handle to a device
/// driver. However, when you open a communications resource, such as a serial port, you must specify exclusive access. Use the other
/// CreateFile parameters as follows when opening a device handle:
///
///
/// -
/// The fdwCreate parameter must specify OPEN_EXISTING.
///
/// -
/// The hTemplateFile parameter must be NULL.
///
/// -
///
/// The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped
/// (asynchronous) I/O operations.
///
///
///
///
[Obsolete("Use 'Task DeviceIoControlAsync(HFILE hDevice, uint ioControlCode, TIn? inVal)' instead.")]
public static Task DeviceIoControlAsync(HFILE hDevice, uint ioControlCode, TIn? inVal, TOut? outVal) where TIn : struct where TOut : struct =>
new TaskFactory().FromAsync(BeginDeviceIoControl, EndDeviceIoControl, hDevice, ioControlCode, Pack(inVal, outVal), null);
///
/// Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
///
/// The type of the .
/// The type of the return value.
///
/// A handle to the device on which the operation is to be performed. The device is typically a volume, directory, file, or stream.
/// To retrieve a device handle, use the CreateFile function. For more information, see Remarks.
///
///
/// The control code for the operation. This value identifies the specific operation to be performed and the type of device on which
/// to perform it.
///
///
/// The input value required to perform the operation. The type of this data depends on the value of the parameter.
///
/// An asynchronous result containing the populated data supplied by .
///
///
/// To retrieve a handle to the device, you must call the CreateFile function with either the name of a device or the name of the
/// driver associated with a device. To specify a device name, use the following format:
///
/// \\.\DeviceName
///
/// DeviceIoControl can accept a handle to a specific device. For example, to open a handle to the logical drive
/// A: with CreateFile, specify \\.\a:. Alternatively, you can use the names \\.\PhysicalDrive0, \\.\PhysicalDrive1, and so on, to
/// open handles to the physical drives on a system.
///
///
/// You should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when calling CreateFile to open a handle to a device
/// driver. However, when you open a communications resource, such as a serial port, you must specify exclusive access. Use the other
/// CreateFile parameters as follows when opening a device handle:
///
///
/// -
/// The fdwCreate parameter must specify OPEN_EXISTING.
///
/// -
/// The hTemplateFile parameter must be NULL.
///
/// -
///
/// The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped
/// (asynchronous) I/O operations.
///
///
///
///
public static Task DeviceIoControlAsync(HFILE hDevice, uint ioControlCode, TIn? inVal) where TIn : struct where TOut : struct
{
TOut? outValue = default(TOut);
var buf = Pack(inVal, outValue);
return Task.Factory.FromAsync(BeginDeviceIoControl, EndDeviceIoControl, hDevice, ioControlCode, buf, null);
}
/// Explicits the device io control asynchronous.
/// The type of the in.
/// The type of the out.
/// The h device.
/// The io control code.
/// The in value.
/// The out value.
///
///
private static unsafe Task ExplicitDeviceIoControlAsync(HFILE hDevice, uint ioControlCode, TIn? inVal, TOut? outVal) where TIn : struct where TOut : struct
{
#pragma warning disable CS0618 // Type or member is obsolete
ThreadPool.BindHandle((IntPtr)hDevice);
#pragma warning restore CS0618 // Type or member is obsolete
var tcs = new TaskCompletionSource();
var buffer = Pack(inVal, outVal);
var nativeOverlapped = new Overlapped().Pack((code, bytes, overlap) =>
{
try
{
switch (code)
{
case Win32Error.ERROR_SUCCESS:
outVal = Unpack(buffer).Item2;
tcs.TrySetResult(outVal);
break;
case Win32Error.ERROR_OPERATION_ABORTED:
tcs.TrySetCanceled();
break;
default:
tcs.TrySetException(new Win32Exception((int)code));
break;
}
}
finally
{
Overlapped.Unpack(overlap);
Overlapped.Free(overlap);
}
}, buffer);
var unpack = true;
try
{
var inSz = Marshal.SizeOf(typeof(TIn));
fixed (byte* pIn = buffer, pOut = &buffer[inSz])
{
var ret = DeviceIoControl(hDevice, ioControlCode, pIn, (uint)inSz, pOut, (uint)(buffer.Length - inSz), out var bRet,
nativeOverlapped);
if (ret)
{
outVal = Unpack(buffer).Item2;
tcs.SetResult(outVal);
return tcs.Task;
}
}
var lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error != Win32Error.ERROR_IO_PENDING && lastWin32Error != Win32Error.ERROR_SUCCESS)
throw new Win32Exception(lastWin32Error);
unpack = false;
return tcs.Task;
}
finally
{
if (unpack)
{
Overlapped.Unpack(nativeOverlapped);
Overlapped.Free(nativeOverlapped);
}
}
}
}
}
#endif