ScpControl/Usb/PnP/Win32Usb.cs (122 lines of code) (raw):
using System;
using System.Runtime.InteropServices;
namespace ScpControl.Usb.PnP
{
/// <summary>
/// Class that wraps USB API calls and structures.
/// </summary>
public abstract class Win32Usb
{
#region Structures
/// <summary>
/// An overlapped structure used for overlapped IO operations. The structure is
/// only used by the OS to keep state on pending operations. You don't need to fill anything in if you
/// unless you want a Windows event to fire when the operation is complete.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
protected struct Overlapped
{
public uint Internal;
public uint InternalHigh;
public uint Offset;
public uint OffsetHigh;
public IntPtr Event;
}
/// <summary>
/// Provides details about a single USB device
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
protected struct DeviceInterfaceData
{
public int Size;
public Guid InterfaceClassGuid;
public int Flags;
public UIntPtr Reserved;
}
/// <summary>
/// Provides the capabilities of a HID device
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
protected struct HidCaps
{
public short Usage;
public short UsagePage;
public short InputReportByteLength;
public short OutputReportByteLength;
public short FeatureReportByteLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] public short[] Reserved;
public short NumberLinkCollectionNodes;
public short NumberInputButtonCaps;
public short NumberInputValueCaps;
public short NumberInputDataIndices;
public short NumberOutputButtonCaps;
public short NumberOutputValueCaps;
public short NumberOutputDataIndices;
public short NumberFeatureButtonCaps;
public short NumberFeatureValueCaps;
public short NumberFeatureDataIndices;
}
/// <summary>
/// Access to the path for a device
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DeviceInterfaceDetailData
{
public int Size;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string DevicePath;
}
/// <summary>
/// Used when registering a window to receive messages about devices added or removed from the system.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public class DeviceBroadcastInterface
{
public Guid ClassGuid;
public int DeviceType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string Name;
public int Reserved;
public int Size;
}
#endregion
#region Constants
/// <summary>Windows message sent when a device is inserted or removed</summary>
public const int WM_DEVICECHANGE = 0x0219;
/// <summary>WParam for above : A device was inserted</summary>
public const int DEVICE_ARRIVAL = 0x8000;
/// <summary>WParam for above : A device was removed</summary>
public const int DEVICE_REMOVECOMPLETE = 0x8004;
/// <summary>Used in SetupDiClassDevs to get devices present in the system</summary>
protected const int DIGCF_PRESENT = 0x02;
/// <summary>Used in SetupDiClassDevs to get device interface details</summary>
protected const int DIGCF_DEVICEINTERFACE = 0x10;
/// <summary>Used when registering for device insert/remove messages : specifies the type of device</summary>
protected const int DEVTYP_DEVICEINTERFACE = 0x05;
/// <summary>Used when registering for device insert/remove messages : we're giving the API call a window handle</summary>
protected const int DEVICE_NOTIFY_WINDOW_HANDLE = 0;
/// <summary>Purges Win32 transmit buffer by aborting the current transmission.</summary>
protected const uint PURGE_TXABORT = 0x01;
/// <summary>Purges Win32 receive buffer by aborting the current receive.</summary>
protected const uint PURGE_RXABORT = 0x02;
/// <summary>Purges Win32 transmit buffer by clearing it.</summary>
protected const uint PURGE_TXCLEAR = 0x04;
/// <summary>Purges Win32 receive buffer by clearing it.</summary>
protected const uint PURGE_RXCLEAR = 0x08;
/// <summary>CreateFile : Open file for read</summary>
protected const uint GENERIC_READ = 0x80000000;
/// <summary>CreateFile : Open file for write</summary>
protected const uint GENERIC_WRITE = 0x40000000;
/// <summary>CreateFile : file share for write</summary>
protected const uint FILE_SHARE_WRITE = 0x2;
/// <summary>CreateFile : file share for read</summary>
protected const uint FILE_SHARE_READ = 0x1;
/// <summary>CreateFile : Open handle for overlapped operations</summary>
protected const uint FILE_FLAG_OVERLAPPED = 0x40000000;
/// <summary>CreateFile : Resource to be "created" must exist</summary>
protected const uint OPEN_EXISTING = 3;
/// <summary>CreateFile : Resource will be "created" or existing will be used</summary>
protected const uint OPEN_ALWAYS = 4;
/// <summary>ReadFile/WriteFile : Overlapped operation is incomplete.</summary>
protected const uint ERROR_IO_PENDING = 997;
/// <summary>Infinite timeout</summary>
protected const uint INFINITE = 0xFFFFFFFF;
/// <summary>
/// Simple representation of a null handle : a closed stream will get this handle. Note it is public for
/// comparison by higher level classes.
/// </summary>
public static IntPtr NullHandle = IntPtr.Zero;
/// <summary>Simple representation of the handle returned when CreateFile fails.</summary>
protected static IntPtr InvalidHandleValue = new IntPtr(-1);
#endregion
#region P/Invoke
/// <summary>
/// Gets the GUID that Windows uses to represent HID class devices
/// </summary>
/// <param name="gHid">An out parameter to take the Guid</param>
[DllImport("hid.dll", SetLastError = true)]
protected static extern void HidD_GetHidGuid(out Guid gHid);
/// <summary>
/// Allocates an InfoSet memory block within Windows that contains details of devices.
/// </summary>
/// <param name="gClass">Class guid (e.g. HID guid)</param>
/// <param name="strEnumerator">Not used</param>
/// <param name="hParent">Not used</param>
/// <param name="nFlags">Type of device details required (DIGCF_ constants)</param>
/// <returns>A reference to the InfoSet</returns>
[DllImport("setupapi.dll", SetLastError = true)]
protected static extern IntPtr SetupDiGetClassDevs(ref Guid gClass,
[MarshalAs(UnmanagedType.LPStr)] string strEnumerator, IntPtr hParent, uint nFlags);
/// <summary>
/// Frees InfoSet allocated in call to above.
/// </summary>
/// <param name="lpInfoSet">Reference to InfoSet</param>
/// <returns>true if successful</returns>
[DllImport("setupapi.dll", SetLastError = true)]
protected static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);
/// <summary>
/// Gets the DeviceInterfaceData for a device from an InfoSet.
/// </summary>
/// <param name="lpDeviceInfoSet">InfoSet to access</param>
/// <param name="nDeviceInfoData">Not used</param>
/// <param name="gClass">Device class guid</param>
/// <param name="nIndex">Index into InfoSet for device</param>
/// <param name="oInterfaceData">DeviceInterfaceData to fill with data</param>
/// <returns>True if successful, false if not (e.g. when index is passed end of InfoSet)</returns>
[DllImport("setupapi.dll", SetLastError = true)]
protected static extern bool SetupDiEnumDeviceInterfaces(IntPtr lpDeviceInfoSet, uint nDeviceInfoData,
ref Guid gClass, uint nIndex, ref DeviceInterfaceData oInterfaceData);
/// <summary>
/// SetupDiGetDeviceInterfaceDetail
/// Gets the interface detail from a DeviceInterfaceData. This is pretty much the device path.
/// You call this twice, once to get the size of the struct you need to send (nDeviceInterfaceDetailDataSize=0)
/// and once again when you've allocated the required space.
/// </summary>
/// <param name="lpDeviceInfoSet">InfoSet to access</param>
/// <param name="oInterfaceData">DeviceInterfaceData to use</param>
/// <param name="lpDeviceInterfaceDetailData">DeviceInterfaceDetailData to fill with data</param>
/// <param name="nDeviceInterfaceDetailDataSize">The size of the above</param>
/// <param name="nRequiredSize">The required size of the above when above is set as zero</param>
/// <param name="lpDeviceInfoData">Not used</param>
/// <returns></returns>
[DllImport("setupapi.dll", SetLastError = true)]
protected static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr lpDeviceInfoSet,
ref DeviceInterfaceData oInterfaceData, IntPtr lpDeviceInterfaceDetailData,
uint nDeviceInterfaceDetailDataSize, ref uint nRequiredSize, IntPtr lpDeviceInfoData);
#endregion
#region Public methods
/// <summary>
/// Registers a window to receive windows messages when a device is inserted/removed. Need to call this
/// from a form when its handle has been created, not in the form constructor. Use form's OnHandleCreated override.
/// </summary>
/// <param name="hWnd">Handle to window that will receive messages</param>
/// <param name="gClass">Class of devices to get messages for</param>
/// <returns>A handle used when unregistering</returns>
protected static IntPtr RegisterForUsbEvents(IntPtr hWnd, Guid gClass)
{
var retVal = IntPtr.Zero;
return ScpDevice.RegisterNotify(hWnd, gClass, ref retVal) ? retVal : IntPtr.Zero;
}
/// <summary>
/// Unregisters notifications. Can be used in form dispose
/// </summary>
/// <param name="hHandle">Handle returned from RegisterForUSBEvents</param>
/// <returns>True if successful</returns>
protected static bool UnregisterForUsbEvents(IntPtr hHandle)
{
return ScpDevice.UnregisterNotify(hHandle);
}
/// <summary>
/// Helper to get the HID guid.
/// </summary>
protected static Guid HidGuid
{
get
{
Guid gHid;
HidD_GetHidGuid(out gHid);
return gHid;
//return new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); //gHid;
}
}
#endregion
}
}