#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