Skip to content

Commit

Permalink
Add DeviceIoControl and NT status codes
Browse files Browse the repository at this point in the history
  • Loading branch information
Antoine Larine committed Jan 25, 2024
1 parent 3f3e3b3 commit ed939dc
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 35 deletions.
12 changes: 10 additions & 2 deletions src/WinAPI/NativeMethods/ErrorCodes.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Reflection.Emit;

namespace Larin.WinAPI.NativeMethods;

/// <summary>
Expand Down Expand Up @@ -133,6 +131,16 @@ public static class ErrorCodes
/// </summary>
public const int ERROR_ALREADY_EXISTS = 183;

/// <summary>
/// More data is available.
/// </summary>
public const int ERROR_MORE_DATA = 234;

/// <summary>
/// The wait operation timed out.
/// </summary>
public const int WAIT_TIMEOUT = 258;

/// <summary>
/// No more data is available.
/// </summary>
Expand Down
92 changes: 77 additions & 15 deletions src/WinAPI/NativeMethods/Kernel32.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
// Copyright © Anton Larin, 2024. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualBasic;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net.NetworkInformation;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using System.Runtime.Versioning;
using static Larin.WinAPI.NativeMethods.ErrorCodes;
using static System.Collections.Specialized.BitVector32;
using static System.Net.WebRequestMethods;
using static Larin.WinAPI.NativeMethods.NtStatus;

namespace Larin.WinAPI.NativeMethods;

/// <summary>
/// P/Invoke items for the Kernel32.dll Windows API library
/// </summary>
[SupportedOSPlatform("WINDOWS")]
public static class Kernel32
public static unsafe class Kernel32
{
/// <summary>
/// Kernel32 library file name
Expand All @@ -34,6 +24,47 @@ public static class Kernel32
/// </summary>
public const nint INVALID_HANDLE_VALUE = -1;

/// <summary>
/// Contains information used in asynchronous (or overlapped) input and output (I/O).
/// </summary>
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped</remarks>
[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED
{
/// <summary>
/// The status code for the I/O request. When the request is issued, the system sets this member to <see cref="STATUS_PENDING"/> to indicate that the operation has not yet started.
/// When the request is completed, the system sets this member to the status code for the completed request.
/// </summary>
public nuint Internal;

/// <summary>
/// The number of bytes transferred for the I/O request. The system sets this member if the request is completed without errors.
/// </summary>
public nuint InternalHigh;


/// <summary>
/// The low-order portion of the file position at which to start the I/O request, as specified by the user.
/// This member is nonzero only when performing I/O requests on a seeking device that supports the concept of an offset(also referred to as a file pointer mechanism), such as a file.
/// Otherwise, this member must be zero.
/// </summary>
public uint Offset;

/// <summary>
/// The high-order portion of the file position at which to start the I/O request, as specified by the user.
/// This member is nonzero only when performing I/O requests on a seeking device that supports the concept of an offset(also referred to as a file pointer mechanism), such as a file.
/// Otherwise, this member must be zero.
/// </summary>
public uint OffsetHigh;

/// <summary>
/// A handle to the event that will be set to a signaled state by the system when the operation has completed.
/// The user must initialize this member either to zero or a valid event handle using the <see cref="CreateEvent"/> function before passing this structure to any overlapped functions.
/// This event can then be used to synchronize simultaneous I/O requests for a device.
/// </summary>
public nint hEvent;
}

/// <summary>
/// Creates or opens a file or I/O device.
/// </summary>
Expand All @@ -55,7 +86,7 @@ public static extern nint CreateFile(
[In] string lpFileName,
[In] uint dwDesiredAccess,
[In] uint dwShareMode,
[In, Optional] nint lpSecurityAttributes,
[In, Optional] void* lpSecurityAttributes,
[In] uint dwCreationDisposition,
[In] uint dwFlagsAndAttributes,
[In, Optional] nint hTemplateFile
Expand Down Expand Up @@ -430,7 +461,6 @@ public static extern nint CreateFile(
/// </summary>
public const uint SECURITY_EFFECTIVE_ONLY = 0x00080000;


/// <summary>
/// Closes an open object handle.
/// </summary>
Expand All @@ -442,6 +472,38 @@ public static extern nint CloseHandle(
[In] nint hObject
);

/// <summary>
/// Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.
/// </summary>
/// <param name="hDevice">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 <see cref="CreateFile"/> function.</param>
/// <param name="dwIoControlCode">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.</param>
/// <param name="lpInBuffer">A pointer to the input buffer that contains the data required to perform the operation. The format of this data depends on the value of the dwIoControlCode parameter.
/// This parameter can be NULL if dwIoControlCode specifies an operation that does not require input data.</param>
/// <param name="nInBufferSize">The size of the input buffer, in bytes.</param>
/// <param name="lpOutBuffer">A pointer to the output buffer that is to receive the data returned by the operation. The format of this data depends on the value of the dwIoControlCode parameter.
/// This parameter can be NULL if dwIoControlCode specifies an operation that does not return data.</param>
/// <param name="nOutBufferSize">The size of the output buffer, in bytes.</param>
/// <param name="lpBytesReturned">A pointer to a variable that receives the size of the data stored in the output buffer, in bytes.</param>
/// <param name="lpOverlapped">A pointer to an <see cref="OVERLAPPED"/> structure. If hDevice was opened without specifying <see cref="FILE_FLAG_OVERLAPPED"/>, lpOverlapped is ignored.
/// If hDevice was opened with the <see cref="FILE_FLAG_OVERLAPPED"/> flag, the operation is performed as an overlapped(asynchronous) operation.
/// In this case, lpOverlapped must point to a valid <see cref="OVERLAPPED"/> structure that contains a handle to an event object. Otherwise, the function fails in unpredictable ways.
/// For overlapped operations, <see cref="DeviceIoControl"/> returns immediately, and the event object is signaled when the operation has been completed.
/// Otherwise, the function does not return until the operation has been completed or an error occurs.</param>
/// <returns></returns>
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol</remarks>
[DllImport(Kernel32Lib, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool DeviceIoControl(
[In] nint hDevice,
[In] uint dwIoControlCode,
[In, Optional] void* lpInBuffer,
[In] uint nInBufferSize,
[Out, Optional] void* lpOutBuffer,
[In] uint nOutBufferSize,
[Out, Optional] uint* lpBytesReturned,
[In, Out, Optional] OVERLAPPED* lpOverlapped
);

/// <summary>
/// Frees the specified local memory object and invalidates its handle.
/// </summary>
Expand All @@ -450,6 +512,6 @@ [In] nint hObject
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree</remarks>
[DllImport(Kernel32Lib, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern nint LocalFree(
[In] nint hMem
[In] void* hMem
);
}
33 changes: 33 additions & 0 deletions src/WinAPI/NativeMethods/NtStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Larin.WinAPI.NativeMethods;

/// <summary>
/// Windows System Error codes
/// </summary>
/// <remarks>https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55</remarks>
public static class NtStatus
{
/// <summary>
/// The operation completed successfully.
/// </summary>
public const uint STATUS_WAIT_0 = 0x00000000;

/// <summary>
/// The caller attempted to wait for a mutex that has been abandoned.
/// </summary>
public const uint STATUS_ABANDONED_WAIT_0 = 0x00000080;

/// <summary>
/// The delay completed because the thread was alerted.
/// </summary>
public const uint STATUS_ALERTED = 0x00000101;

/// <summary>
/// The given Timeout interval expired.
/// </summary>
public const uint STATUS_TIMEOUT = 0x00000102;

/// <summary>
/// The operation that was requested is pending completion.
/// </summary>
public const uint STATUS_PENDING = 0x00000103;
}
59 changes: 41 additions & 18 deletions src/WinAPI/NativeMethods/SetupAPI.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// Copyright © Anton Larin, 2024. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualBasic;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Principal;
using static Larin.WinAPI.NativeMethods.Crypt32;
using static Larin.WinAPI.NativeMethods.SetupAPI;

namespace Larin.WinAPI.NativeMethods;

Expand Down Expand Up @@ -36,8 +32,8 @@ public static unsafe class SetupAPI
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsw</remarks>
[DllImport(SetupApiLib, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern nint SetupDiGetClassDevs(
[In] nint ClassGuid,
[In] nint Enumerator,
[In] Guid* ClassGuid,
[In] void* Enumerator,
[In] nint hwndParent,
[In] uint Flags
);
Expand Down Expand Up @@ -89,7 +85,7 @@ [In] uint Flags
/// Enumerates the device interfaces that are contained in a device information set.
/// </summary>
/// <param name="hDeviceInfoSet">A pointer to a device information set that contains the device interfaces for which to return information. This handle is typically returned by <see cref="SetupDiGetClassDevs"/>.</param>
/// <param name="DeviceInfoData">A pointer to a <see cref="SP_DEVINFO_DATA"/> structure that specifies a device information element in DeviceInfoSet. This parameter is optional and can be NULL.
/// <param name="pDeviceInfoData">A pointer to a <see cref="SP_DEVINFO_DATA"/> structure that specifies a device information element in DeviceInfoSet. This parameter is optional and can be NULL.
/// If this parameter is specified, <see cref="SetupDiEnumDeviceInterfaces"/> constrains the enumeration to the interfaces that are supported by the specified device.
/// If this parameter is NULL, repeated calls to <see cref="SetupDiEnumDeviceInterfaces"/> return information about the interfaces that are associated with all the device information elements in DeviceInfoSet.
/// This pointer is typically returned by <see cref="SetupDiEnumDeviceInfo"/>.</param>
Expand All @@ -100,13 +96,14 @@ [In] uint Flags
/// <param name="DeviceInterfaceData">A pointer to a caller-allocated buffer that contains, on successful return, a completed <see cref="SP_DEVICE_INTERFACE_DATA"/> structure that identifies an interface that meets the search parameters.
/// The caller must set DeviceInterfaceData.cbSize to sizeof(SP_DEVICE_INTERFACE_DATA) before calling this function.</param>
/// <returns>Returns TRUE if the function completed without error. If the function completed with an error, FALSE is returned and the error code for the failure can be retrieved by calling GetLastError.</returns>
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinterfaces</remarks>
[DllImport(SetupApiLib, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool SetupDiEnumDeviceInterfaces(
[In] nint hDeviceInfoSet,
[In] nint DeviceInfoData,
[In] nint InterfaceClassGuid,
[In, Optional] SP_DEVINFO_DATA* pDeviceInfoData,
[In] Guid* InterfaceClassGuid,
[In] uint MemberIndex,
[Out] nint DeviceInterfaceData
[Out] SP_DEVICE_INTERFACE_DATA* DeviceInterfaceData
);

/// <summary>
Expand Down Expand Up @@ -202,27 +199,53 @@ public SP_DEVICE_INTERFACE_DATA()
/// Returns details about a device interface.
/// </summary>
/// <param name="hDeviceInfoSet">A pointer to the device information set that contains the interface for which to retrieve details. This handle is typically returned by <see cref="SetupDiGetClassDevs"/>.</param>
/// <param name="DeviceInterfaceData">A pointer to an <see cref="SP_DEVICE_INTERFACE_DATA"/> structure that specifies the interface in DeviceInfoSet for which to retrieve details.
/// <param name="pDeviceInterfaceData">A pointer to an <see cref="SP_DEVICE_INTERFACE_DATA"/> structure that specifies the interface in DeviceInfoSet for which to retrieve details.
/// A pointer of this type is typically returned by <see cref="SetupDiEnumDeviceInterfaces"/>.</param>
/// <param name="DeviceInterfaceDetailData">A pointer to an <see cref="SP_DEVICE_INTERFACE_DETAIL_DATA"/> structure to receive information about the specified interface. This parameter is optional and can be NULL.
/// <param name="pDeviceInterfaceDetailData">A pointer to an <see cref="SP_DEVICE_INTERFACE_DETAIL_DATA_W"/> structure to receive information about the specified interface. This parameter is optional and can be NULL.
/// This parameter must be NULL if DeviceInterfaceDetailSize is zero. If this parameter is specified, the caller must set DeviceInterfaceDetailData.cbSize to sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) before calling this function.
/// The cbSize member always contains the size of the fixed part of the data structure, not a size reflecting the variable-length string at the end.</param>
/// <param name="DeviceInterfaceDetailDataSize">The size of the DeviceInterfaceDetailData buffer. The buffer must be at least (offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + sizeof(TCHAR)) bytes, to contain the fixed part of the structure and a single NULL to terminate an empty MULTI_SZ string.
/// This parameter must be zero if DeviceInterfaceDetailData is NULL.</param>
/// <param name="RequiredSize">A pointer to a variable of type DWORD that receives the required size of the DeviceInterfaceDetailData buffer. This size includes the size of the fixed part of the structure plus the number of bytes required for the variable-length device path string. This parameter is optional and can be NULL.</param>
/// <param name="DeviceInfoData">A pointer to a buffer that receives information about the device that supports the requested interface. The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA). This parameter is optional and can be NULL.</param>
/// <param name="pRequiredSize">A pointer to a variable of type DWORD that receives the required size of the DeviceInterfaceDetailData buffer. This size includes the size of the fixed part of the structure plus the number of bytes required for the variable-length device path string. This parameter is optional and can be NULL.</param>
/// <param name="pDeviceInfoData">A pointer to a buffer that receives information about the device that supports the requested interface. The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA). This parameter is optional and can be NULL.</param>
/// <returns>Returns TRUE if the function completed without error. If the function completed with an error, FALSE is returned and the error code for the failure can be retrieved by calling GetLastError.</returns>
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetdeviceinterfacedetailw</remarks>
[DllImport(SetupApiLib, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
[In] nint hDeviceInfoSet,
[In] nint DeviceInterfaceData,
[Out, Optional] nint DeviceInterfaceDetailData,
[In] SP_DEVICE_INTERFACE_DATA* pDeviceInterfaceData,
[Out, Optional] void* pDeviceInterfaceDetailData,
[In] uint DeviceInterfaceDetailDataSize,
[Out, Optional] nint RequiredSize,
[Out, Optional] nint DeviceInfoData
[Out, Optional] uint* pRequiredSize,
[Out, Optional] void* pDeviceInfoData
);

/// <summary>
/// Contains the path for a device interface.
/// </summary>
/// <remarks>https://learn.microsoft.com/en-us/windows/win32/api/setupapi/ns-setupapi-sp_device_interface_detail_data_w</remarks>
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA_W
{
/// <summary>
/// The size, in bytes, of the <see cref="SP_DEVICE_INTERFACE_DETAIL_DATA_W"/> structure.
/// </summary>
public uint cbSize;

/// <summary>
/// A NULL-terminated string that contains the device interface path. This path can be passed to Win32 functions such as CreateFile.
/// </summary>
public char DevicePath;

/// <summary>
/// Initializes a new instance of the <see cref="SP_DEVICE_INTERFACE_DETAIL_DATA_W"/> structure
/// </summary>
public SP_DEVICE_INTERFACE_DETAIL_DATA_W()
{
cbSize = (uint)Marshal.SizeOf(this);
}
}

/// <summary>
/// Deletes a device information set and frees all associated memory.
/// </summary>
Expand Down

0 comments on commit ed939dc

Please sign in to comment.