From be52156170e0e15e8546673c3e1d21678d0e7dac Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 6 Dec 2017 19:30:03 +0000 Subject: [PATCH] Added code to retrieve USB IDs and strings for Windows. --- DiscImageChef.Devices/Device/Constructor.cs | 19 + .../DiscImageChef.Devices.csproj | 2 + DiscImageChef.Devices/Windows/Enums.cs | 71 + DiscImageChef.Devices/Windows/Extern.cs | 2 +- DiscImageChef.Devices/Windows/Structs.cs | 17 + DiscImageChef.Devices/Windows/Usb.cs | 1260 +++++++++++++++++ DiscImageChef.Devices/Windows/UsbFunctions.cs | 316 +++++ 7 files changed, 1686 insertions(+), 1 deletion(-) create mode 100644 DiscImageChef.Devices/Windows/Usb.cs create mode 100644 DiscImageChef.Devices/Windows/UsbFunctions.cs diff --git a/DiscImageChef.Devices/Device/Constructor.cs b/DiscImageChef.Devices/Device/Constructor.cs index 051010e0..7a15f68a 100644 --- a/DiscImageChef.Devices/Device/Constructor.cs +++ b/DiscImageChef.Devices/Device/Constructor.cs @@ -341,7 +341,26 @@ namespace DiscImageChef.Devices } else if(platformID == Interop.PlatformID.Win32NT) { + Windows.Usb.USBDevice usbDevice = null; + // I have to search for USB disks, floppies and CD-ROMs as separate device types + foreach(string devGuid in new [] { Windows.Usb.GUID_DEVINTERFACE_FLOPPY, Windows.Usb.GUID_DEVINTERFACE_CDROM , Windows.Usb.GUID_DEVINTERFACE_DISK }) + { + usbDevice = Windows.Usb.FindDrivePath(devicePath, devGuid); + if (usbDevice != null) + break; + } + + if(usbDevice != null) + { + // TODO: Get binary descriptors + usbVendor = (ushort)usbDevice.DeviceDescriptor.idVendor; + usbProduct = (ushort)usbDevice.DeviceDescriptor.idProduct; + usbManufacturerString = usbDevice.Manufacturer; + usbProductString = usbDevice.Product; + usbSerialString = usbDevice.SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number + } + } // TODO: Implement for other operating systems else diff --git a/DiscImageChef.Devices/DiscImageChef.Devices.csproj b/DiscImageChef.Devices/DiscImageChef.Devices.csproj index 79ee6228..5bb9b6af 100644 --- a/DiscImageChef.Devices/DiscImageChef.Devices.csproj +++ b/DiscImageChef.Devices/DiscImageChef.Devices.csproj @@ -84,6 +84,8 @@ + + diff --git a/DiscImageChef.Devices/Windows/Enums.cs b/DiscImageChef.Devices/Windows/Enums.cs index 4e371b82..51b175f9 100644 --- a/DiscImageChef.Devices/Windows/Enums.cs +++ b/DiscImageChef.Devices/Windows/Enums.cs @@ -42,70 +42,141 @@ namespace DiscImageChef.Devices.Windows /// FILE_ATTRIBUTE_ARCHIVE /// Archive = 0x20, + /// /// FILE_ATTRIBUTE_COMPRESSED /// Compressed = 0x800, + /// /// FILE_ATTRIBUTE_DEVICE /// Device = 0x40, + /// /// FILE_ATTRIBUTE_DIRECTORY /// Directory = 0x10, + /// /// FILE_ATTRIBUTE_ENCRYPTED /// Encrypted = 0x4000, + /// /// FILE_ATTRIBUTE_HIDDEN /// Hidden = 0x02, + /// /// FILE_ATTRIBUTE_INTEGRITY_STREAM /// IntegrityStream = 0x8000, + /// /// FILE_ATTRIBUTE_NORMAL /// Normal = 0x80, + /// /// FILE_ATTRIBUTE_NOT_CONTENT_INDEXED /// NotContentIndexed = 0x2000, + /// /// FILE_ATTRIBUTE_NO_SCRUB_DATA /// NoScrubData = 0x20000, + /// /// FILE_ATTRIBUTE_OFFLINE /// Offline = 0x1000, + /// /// FILE_ATTRIBUTE_READONLY /// Readonly = 0x01, + /// /// FILE_ATTRIBUTE_REPARSE_POINT /// ReparsePoint = 0x400, + /// /// FILE_ATTRIBUTE_SPARSE_FILE /// SparseFile = 0x200, + /// /// FILE_ATTRIBUTE_SYSTEM /// System = 0x04, + /// /// FILE_ATTRIBUTE_TEMPORARY /// Temporary = 0x100, + /// /// FILE_ATTRIBUTE_VIRTUAL /// Virtual = 0x10000, + + /// + /// FILE_FLAG_BACKUP_SEMANTICS + /// + BackupSemantics = 0x02000000, + + /// + /// FILE_FLAG_DELETE_ON_CLOSE + /// + DeleteOnClose = 0x04000000, + + /// + /// FILE_FLAG_NO_BUFFERING + /// + NoBuffering = 0x20000000, + + /// + /// FILE_FLAG_OPEN_NO_RECALL + /// + OpenNoRecall = 0x00100000, + + /// + /// FILE_FLAG_OPEN_REPARSE_POINT + /// + OpenReparsePoint = 0x00200000, + + /// + /// FILE_FLAG_OVERLAPPED + /// + Overlapped = 0x40000000, + + /// + /// FILE_FLAG_POSIX_SEMANTICS + /// + PosixSemantics = 0x0100000, + + /// + /// FILE_FLAG_RANDOM_ACCESS + /// + RandomAccess = 0x10000000, + + /// + /// FILE_FLAG_SESSION_AWARE + /// + SessionAware = 0x00800000, + + /// + /// FILE_FLAG_SEQUENTIAL_SCAN + /// + SequentialScan = 0x08000000, + + /// + /// FILE_FLAG_WRITE_THROUGH + /// + WriteThrough = 0x80000000, } [Flags] diff --git a/DiscImageChef.Devices/Windows/Extern.cs b/DiscImageChef.Devices/Windows/Extern.cs index 44936be2..cb7a7702 100644 --- a/DiscImageChef.Devices/Windows/Extern.cs +++ b/DiscImageChef.Devices/Windows/Extern.cs @@ -140,7 +140,7 @@ namespace DiscImageChef.Devices.Windows [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool SetupDiDestroyDeviceInfoList(SafeFileHandle hDevInfo); - + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool CloseHandle(SafeFileHandle hDevice); } diff --git a/DiscImageChef.Devices/Windows/Structs.cs b/DiscImageChef.Devices/Windows/Structs.cs index 627ccaba..3eeacfc6 100644 --- a/DiscImageChef.Devices/Windows/Structs.cs +++ b/DiscImageChef.Devices/Windows/Structs.cs @@ -238,5 +238,22 @@ namespace DiscImageChef.Devices.Windows public uint flags; private IntPtr reserved; } + + [StructLayout(LayoutKind.Sequential)] + struct USB_SETUP_PACKET + { + public byte bmRequest; + public byte bRequest; + public short wValue; + public short wIndex; + public short wLength; + } + [StructLayout(LayoutKind.Sequential)] + struct USB_DESCRIPTOR_REQUEST + { + public int ConnectionIndex; + public USB_SETUP_PACKET SetupPacket; + //public byte[] Data; + } } diff --git a/DiscImageChef.Devices/Windows/Usb.cs b/DiscImageChef.Devices/Windows/Usb.cs new file mode 100644 index 00000000..45fa1f07 --- /dev/null +++ b/DiscImageChef.Devices/Windows/Usb.cs @@ -0,0 +1,1260 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Diagnostics; + +// Copyright "Fort Hood TX", public domain, 2007 +namespace DiscImageChef.Devices.Windows +{ + public partial class Usb + { + #region "API Region" + + // ********************** Constants ************************ + + const int GENERIC_WRITE = 0x40000000; + const int FILE_SHARE_READ = 0x1; + const int FILE_SHARE_WRITE = 0x2; + const int OPEN_EXISTING = 0x3; + const int INVALID_HANDLE_VALUE = -1; + + const int IOCTL_GET_HCD_DRIVERKEY_NAME = 0x220424; + const int IOCTL_USB_GET_ROOT_HUB_NAME = 0x220408; + const int IOCTL_USB_GET_NODE_INFORMATION = 0x220408; // same as above... strange, eh? + const int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = 0x220448; + const int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 0x220410; + const int IOCTL_USB_GET_NODE_CONNECTION_NAME = 0x220414; + const int IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = 0x220420; + + const int USB_DEVICE_DESCRIPTOR_TYPE = 0x1; + const int USB_STRING_DESCRIPTOR_TYPE = 0x3; + + const int BUFFER_SIZE = 2048; + const int MAXIMUM_USB_STRING_LENGTH = 255; + + const string GUID_DEVINTERFACE_HUBCONTROLLER = "3abf6f2d-71c4-462a-8a92-1e6861e6af27"; + const string REGSTR_KEY_USB = "USB"; + const int DIGCF_PRESENT = 0x2; + const int DIGCF_ALLCLASSES = 0x4; + const int DIGCF_DEVICEINTERFACE = 0x10; + const int SPDRP_DRIVER = 0x9; + const int SPDRP_DEVICEDESC = 0x0; + const int REG_SZ = 1; + + // ********************** Enumerations ************************ + + //typedef enum _USB_HUB_NODE { + // UsbHub, + // UsbMIParent + //} USB_HUB_NODE; + enum USB_HUB_NODE + { + UsbHub, + UsbMIParent + } + + //typedef enum _USB_CONNECTION_STATUS { + // NoDeviceConnected, + // DeviceConnected, + // DeviceFailedEnumeration, + // DeviceGeneralFailure, + // DeviceCausedOvercurrent, + // DeviceNotEnoughPower, + // DeviceNotEnoughBandwidth, + // DeviceHubNestedTooDeeply, + // DeviceInLegacyHub + //} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + enum USB_CONNECTION_STATUS + { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub + } + + //typedef enum _USB_DEVICE_SPEED { + // UsbLowSpeed = 0, + // UsbFullSpeed, + // UsbHighSpeed + //} USB_DEVICE_SPEED; + enum USB_DEVICE_SPEED : byte + { + UsbLowSpeed, + UsbFullSpeed, + UsbHighSpeed + } + + // ********************** Stuctures ************************ + + //typedef struct _SP_DEVINFO_DATA { + // DWORD cbSize; + // GUID ClassGuid; + // DWORD DevInst; + // ULONG_PTR Reserved; + //} SP_DEVINFO_DATA, *PSP_DEVINFO_DATA; + [StructLayout(LayoutKind.Sequential)] + struct SP_DEVINFO_DATA + { + public int cbSize; + public Guid ClassGuid; + public IntPtr DevInst; + public IntPtr Reserved; + } + + //typedef struct _SP_DEVICE_INTERFACE_DATA { + // DWORD cbSize; + // GUID InterfaceClassGuid; + // DWORD Flags; + // ULONG_PTR Reserved; + //} SP_DEVICE_INTERFACE_DATA, *PSP_DEVICE_INTERFACE_DATA; + [StructLayout(LayoutKind.Sequential)] + struct SP_DEVICE_INTERFACE_DATA + { + public int cbSize; + public Guid InterfaceClassGuid; + public int Flags; + public IntPtr Reserved; + } + + //typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA { + // DWORD cbSize; + // TCHAR DevicePath[ANYSIZE_ARRAY]; + //} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct SP_DEVICE_INTERFACE_DETAIL_DATA + { + public int cbSize; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] public string DevicePath; + } + + //typedef struct _USB_HCD_DRIVERKEY_NAME { + // ULONG ActualLength; + // WCHAR DriverKeyName[1]; + //} USB_HCD_DRIVERKEY_NAME, *PUSB_HCD_DRIVERKEY_NAME; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct USB_HCD_DRIVERKEY_NAME + { + public int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] public string DriverKeyName; + } + + //typedef struct _USB_ROOT_HUB_NAME { + // ULONG ActualLength; + // WCHAR RootHubName[1]; + //} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct USB_ROOT_HUB_NAME + { + public int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] public string RootHubName; + } + + //typedef struct _USB_HUB_DESCRIPTOR { + // UCHAR bDescriptorLength; + // UCHAR bDescriptorType; + // UCHAR bNumberOfPorts; + // USHORT wHubCharacteristics; + // UCHAR bPowerOnToPowerGood; + // UCHAR bHubControlCurrent; + // UCHAR bRemoveAndPowerMask[64]; + //} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct USB_HUB_DESCRIPTOR + { + public byte bDescriptorLength; + public byte bDescriptorType; + public byte bNumberOfPorts; + public short wHubCharacteristics; + public byte bPowerOnToPowerGood; + public byte bHubControlCurrent; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public byte[] bRemoveAndPowerMask; + } + + //typedef struct _USB_HUB_INFORMATION { + // USB_HUB_DESCRIPTOR HubDescriptor; + // BOOLEAN HubIsBusPowered; + //} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + [StructLayout(LayoutKind.Sequential)] + struct USB_HUB_INFORMATION + { + public USB_HUB_DESCRIPTOR HubDescriptor; + public byte HubIsBusPowered; + } + + //typedef struct _USB_NODE_INFORMATION { + // USB_HUB_NODE NodeType; + // union { + // USB_HUB_INFORMATION HubInformation; + // USB_MI_PARENT_INFORMATION MiParentInformation; + // } u; + //} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + [StructLayout(LayoutKind.Sequential)] + struct USB_NODE_INFORMATION + { + public int NodeType; + public USB_HUB_INFORMATION HubInformation; // Yeah, I'm assuming we'll just use the first form + } + + //typedef struct _USB_NODE_CONNECTION_INFORMATION_EX { + // ULONG ConnectionIndex; + // USB_DEVICE_DESCRIPTOR DeviceDescriptor; + // UCHAR CurrentConfigurationValue; + // UCHAR Speed; + // BOOLEAN DeviceIsHub; + // USHORT DeviceAddress; + // ULONG NumberOfOpenPipes; + // USB_CONNECTION_STATUS ConnectionStatus; + // USB_PIPE_INFO PipeList[0]; + //} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct USB_NODE_CONNECTION_INFORMATION_EX + { + public int ConnectionIndex; + public USB_DEVICE_DESCRIPTOR DeviceDescriptor; + public byte CurrentConfigurationValue; + public byte Speed; + public byte DeviceIsHub; + public short DeviceAddress; + public int NumberOfOpenPipes; + + public int ConnectionStatus; + //public IntPtr PipeList; + } + + //typedef struct _USB_DEVICE_DESCRIPTOR { + // UCHAR bLength; + // UCHAR bDescriptorType; + // USHORT bcdUSB; + // UCHAR bDeviceClass; + // UCHAR bDeviceSubClass; + // UCHAR bDeviceProtocol; + // UCHAR bMaxPacketSize0; + // USHORT idVendor; + // USHORT idProduct; + // USHORT bcdDevice; + // UCHAR iManufacturer; + // UCHAR iProduct; + // UCHAR iSerialNumber; + // UCHAR bNumConfigurations; + //} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR ; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct USB_DEVICE_DESCRIPTOR + { + public byte bLength; + public byte bDescriptorType; + public short bcdUSB; + public byte bDeviceClass; + public byte bDeviceSubClass; + public byte bDeviceProtocol; + public byte bMaxPacketSize0; + public short idVendor; + public short idProduct; + public short bcdDevice; + public byte iManufacturer; + public byte iProduct; + public byte iSerialNumber; + public byte bNumConfigurations; + } + + //typedef struct _USB_STRING_DESCRIPTOR { + // UCHAR bLength; + // UCHAR bDescriptorType; + // WCHAR bString[1]; + //} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct USB_STRING_DESCRIPTOR + { + public byte bLength; + public byte bDescriptorType; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXIMUM_USB_STRING_LENGTH)] public string bString; + } + + //typedef struct _USB_DESCRIPTOR_REQUEST { + // ULONG ConnectionIndex; + // struct { + // UCHAR bmRequest; + // UCHAR bRequest; + // USHORT wValue; + // USHORT wIndex; + // USHORT wLength; + // } SetupPacket; + // UCHAR Data[0]; + //} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST + [StructLayout(LayoutKind.Sequential)] + struct USB_SETUP_PACKET + { + public byte bmRequest; + public byte bRequest; + public short wValue; + public short wIndex; + public short wLength; + } + + [StructLayout(LayoutKind.Sequential)] + struct USB_DESCRIPTOR_REQUEST + { + public int ConnectionIndex; + + public USB_SETUP_PACKET SetupPacket; + //public byte[] Data; + } + + //typedef struct _USB_NODE_CONNECTION_NAME { + // ULONG ConnectionIndex; + // ULONG ActualLength; + // WCHAR NodeName[1]; + //} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct USB_NODE_CONNECTION_NAME + { + public int ConnectionIndex; + public int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] public string NodeName; + } + + //typedef struct _USB_NODE_CONNECTION_DRIVERKEY_NAME { + // ULONG ConnectionIndex; + // ULONG ActualLength; + // WCHAR DriverKeyName[1]; + //} USB_NODE_CONNECTION_DRIVERKEY_NAME, *PUSB_NODE_CONNECTION_DRIVERKEY_NAME; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct USB_NODE_CONNECTION_DRIVERKEY_NAME // Yes, this is the same as the structure above... + { + public int ConnectionIndex; + public int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] public string DriverKeyName; + } + + // ********************** API Definitions ************************ + + //HDEVINFO SetupDiGetClassDevs( + // const GUID* ClassGuid, + // PCTSTR Enumerator, + // HWND hwndParent, + // DWORD Flags + //); + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static extern IntPtr SetupDiGetClassDevs( // 1st form using a ClassGUID + ref Guid ClassGuid, + int Enumerator, + IntPtr hwndParent, + int Flags + ); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] // 2nd form uses an Enumerator + static extern IntPtr SetupDiGetClassDevs( + int ClassGuid, + string Enumerator, + IntPtr hwndParent, + int Flags + ); + + //BOOL SetupDiEnumDeviceInterfaces( + // HDEVINFO DeviceInfoSet, + // PSP_DEVINFO_DATA DeviceInfoData, + // const GUID* InterfaceClassGuid, + // DWORD MemberIndex, + // PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData + //); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiEnumDeviceInterfaces( + IntPtr DeviceInfoSet, + IntPtr DeviceInfoData, + ref Guid InterfaceClassGuid, + int MemberIndex, + ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData + ); + + //BOOL SetupDiGetDeviceInterfaceDetail( + // HDEVINFO DeviceInfoSet, + // PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + // PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, + // DWORD DeviceInterfaceDetailDataSize, + // PDWORD RequiredSize, + // PSP_DEVINFO_DATA DeviceInfoData + //); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceInterfaceDetail( + IntPtr DeviceInfoSet, + ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, + int DeviceInterfaceDetailDataSize, + ref int RequiredSize, + ref SP_DEVINFO_DATA DeviceInfoData + ); + + //BOOL SetupDiGetDeviceRegistryProperty( + // HDEVINFO DeviceInfoSet, + // PSP_DEVINFO_DATA DeviceInfoData, + // DWORD Property, + // PDWORD PropertyRegDataType, + // PBYTE PropertyBuffer, + // DWORD PropertyBufferSize, + // PDWORD RequiredSize + //); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceRegistryProperty( + IntPtr DeviceInfoSet, + ref SP_DEVINFO_DATA DeviceInfoData, + int iProperty, + ref int PropertyRegDataType, + IntPtr PropertyBuffer, + int PropertyBufferSize, + ref int RequiredSize + ); + + //BOOL SetupDiEnumDeviceInfo( + // HDEVINFO DeviceInfoSet, + // DWORD MemberIndex, + // PSP_DEVINFO_DATA DeviceInfoData + //); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiEnumDeviceInfo( + IntPtr DeviceInfoSet, + int MemberIndex, + ref SP_DEVINFO_DATA DeviceInfoData + ); + + //BOOL SetupDiDestroyDeviceInfoList( + // HDEVINFO DeviceInfoSet + //); + [DllImport("setupapi.dll", SetLastError = true)] + static extern bool SetupDiDestroyDeviceInfoList( + IntPtr DeviceInfoSet + ); + + //WINSETUPAPI BOOL WINAPI SetupDiGetDeviceInstanceId( + // IN HDEVINFO DeviceInfoSet, + // IN PSP_DEVINFO_DATA DeviceInfoData, + // OUT PTSTR DeviceInstanceId, + // IN DWORD DeviceInstanceIdSize, + // OUT PDWORD RequiredSize OPTIONAL + //); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceInstanceId( + IntPtr DeviceInfoSet, + ref SP_DEVINFO_DATA DeviceInfoData, + StringBuilder DeviceInstanceId, + int DeviceInstanceIdSize, + out int RequiredSize + ); + + //BOOL DeviceIoControl( + // HANDLE hDevice, + // DWORD dwIoControlCode, + // LPVOID lpInBuffer, + // DWORD nInBufferSize, + // LPVOID lpOutBuffer, + // DWORD nOutBufferSize, + // LPDWORD lpBytesReturned, + // LPOVERLAPPED lpOverlapped + //); + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool DeviceIoControl( + IntPtr hDevice, + int dwIoControlCode, + IntPtr lpInBuffer, + int nInBufferSize, + IntPtr lpOutBuffer, + int nOutBufferSize, + out int lpBytesReturned, + IntPtr lpOverlapped + ); + + //HANDLE CreateFile( + // LPCTSTR lpFileName, + // DWORD dwDesiredAccess, + // DWORD dwShareMode, + // LPSECURITY_ATTRIBUTES lpSecurityAttributes, + // DWORD dwCreationDisposition, + // DWORD dwFlagsAndAttributes, + // HANDLE hTemplateFile + //); + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern IntPtr CreateFile( + string lpFileName, + int dwDesiredAccess, + int dwShareMode, + IntPtr lpSecurityAttributes, + int dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + //BOOL CloseHandle( + // HANDLE hObject + //); + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool CloseHandle( + IntPtr hObject + ); + + #endregion + + // + // Return a list of USB Host Controllers + // + static public System.Collections.ObjectModel.ReadOnlyCollection GetHostControllers() + { + List HostList = new List(); + Guid HostGUID = new Guid(GUID_DEVINTERFACE_HUBCONTROLLER); + + // We start at the "root" of the device tree and look for all + // devices that match the interface GUID of a Hub Controller + IntPtr h = SetupDiGetClassDevs(ref HostGUID, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); + bool Success; + int i = 0; + do + { + USBController host = new USBController(); + host.ControllerIndex = i; + + // create a Device Interface Data structure + SP_DEVICE_INTERFACE_DATA dia = new SP_DEVICE_INTERFACE_DATA(); + dia.cbSize = Marshal.SizeOf(dia); + + // start the enumeration + Success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref HostGUID, i, ref dia); + if(Success) + { + // build a DevInfo Data structure + SP_DEVINFO_DATA da = new SP_DEVINFO_DATA(); + da.cbSize = Marshal.SizeOf(da); + + // build a Device Interface Detail Data structure + SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA(); + didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // trust me :) + + // now we can get some more detailed information + int nRequiredSize = 0; + int nBytes = BUFFER_SIZE; + if(SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, nBytes, ref nRequiredSize, ref da)) + { + host.ControllerDevicePath = didd.DevicePath; + + // get the Device Description and DriverKeyName + int RequiredSize = 0; + int RegType = REG_SZ; + + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DEVICEDESC, ref RegType, ptrBuf, + BUFFER_SIZE, ref RequiredSize)) + { + host.ControllerDeviceDesc = Marshal.PtrToStringAuto(ptrBuf); + } + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref RegType, ptrBuf, + BUFFER_SIZE, ref RequiredSize)) + { + host.ControllerDriverKeyName = Marshal.PtrToStringAuto(ptrBuf); + } + } + HostList.Add(host); + } + i++; + } while(Success); + + Marshal.FreeHGlobal(ptrBuf); + SetupDiDestroyDeviceInfoList(h); + } + + // convert it into a Collection + return new System.Collections.ObjectModel.ReadOnlyCollection(HostList); + } + + // + // The USB Host Controller Class + // + public class USBController + { + internal int ControllerIndex; + internal string ControllerDriverKeyName, ControllerDevicePath, ControllerDeviceDesc; + + // A simple default constructor + public USBController() + { + ControllerIndex = 0; + ControllerDevicePath = ""; + ControllerDeviceDesc = ""; + ControllerDriverKeyName = ""; + } + + // Return the index of the instance + public int Index + { + get { return ControllerIndex; } + } + + // Return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0&58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}" + public string DevicePath + { + get { return ControllerDevicePath; } + } + + // The DriverKeyName may be useful as a search key + public string DriverKeyName + { + get { return ControllerDriverKeyName; } + } + + // Return the Friendly Name, such as "VIA USB Enhanced Host Controller" + public string Name + { + get { return ControllerDeviceDesc; } + } + + // Return Root Hub for this Controller + public USBHub GetRootHub() + { + IntPtr h, h2; + USBHub Root = new USBHub(); + Root.HubIsRootHub = true; + Root.HubDeviceDesc = "Root Hub"; + + // Open a handle to the Host Controller + h = CreateFile(ControllerDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + int nBytesReturned; + USB_ROOT_HUB_NAME HubName = new USB_ROOT_HUB_NAME(); + int nBytes = Marshal.SizeOf(HubName); + IntPtr ptrHubName = Marshal.AllocHGlobal(nBytes); + + // get the Hub Name + if(DeviceIoControl(h, IOCTL_USB_GET_ROOT_HUB_NAME, ptrHubName, nBytes, ptrHubName, nBytes, + out nBytesReturned, IntPtr.Zero)) + { + HubName = (USB_ROOT_HUB_NAME)Marshal.PtrToStructure(ptrHubName, typeof(USB_ROOT_HUB_NAME)); + Root.HubDevicePath = @"\\.\" + HubName.RootHubName; + } + + // TODO: Get DriverKeyName for Root Hub + + // Now let's open the Hub (based upon the HubName we got above) + h2 = CreateFile(Root.HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + if(h2.ToInt32() != INVALID_HANDLE_VALUE) + { + USB_NODE_INFORMATION NodeInfo = new USB_NODE_INFORMATION(); + NodeInfo.NodeType = (int)USB_HUB_NODE.UsbHub; + nBytes = Marshal.SizeOf(NodeInfo); + IntPtr ptrNodeInfo = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(NodeInfo, ptrNodeInfo, true); + + // get the Hub Information + if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, ptrNodeInfo, nBytes, ptrNodeInfo, nBytes, + out nBytesReturned, IntPtr.Zero)) + { + NodeInfo = (USB_NODE_INFORMATION)Marshal.PtrToStructure(ptrNodeInfo, + typeof(USB_NODE_INFORMATION)); + Root.HubIsBusPowered = Convert.ToBoolean(NodeInfo.HubInformation.HubIsBusPowered); + Root.HubPortCount = NodeInfo.HubInformation.HubDescriptor.bNumberOfPorts; + } + Marshal.FreeHGlobal(ptrNodeInfo); + CloseHandle(h2); + } + + Marshal.FreeHGlobal(ptrHubName); + CloseHandle(h); + } + return Root; + } + } + + // + // The Hub class + // + public class USBHub + { + internal int HubPortCount; + internal string HubDriverKey, HubDevicePath, HubDeviceDesc; + internal string HubManufacturer, HubProduct, HubSerialNumber, HubInstanceID; + internal bool HubIsBusPowered, HubIsRootHub; + + // a simple default constructor + public USBHub() + { + HubPortCount = 0; + HubDevicePath = ""; + HubDeviceDesc = ""; + HubDriverKey = ""; + HubIsBusPowered = false; + HubIsRootHub = false; + HubManufacturer = ""; + HubProduct = ""; + HubSerialNumber = ""; + HubInstanceID = ""; + } + + // return Port Count + public int PortCount + { + get { return HubPortCount; } + } + + // return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0&58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}" + public string DevicePath + { + get { return HubDevicePath; } + } + + // The DriverKey may be useful as a search key + public string DriverKey + { + get { return HubDriverKey; } + } + + // return the Friendly Name, such as "VIA USB Enhanced Host Controller" + public string Name + { + get { return HubDeviceDesc; } + } + + // the device path of this device + public string InstanceID + { + get { return HubInstanceID; } + } + + // is is this a self-powered hub? + public bool IsBusPowered + { + get { return HubIsBusPowered; } + } + + // is this a root hub? + public bool IsRootHub + { + get { return HubIsRootHub; } + } + + public string Manufacturer + { + get { return HubManufacturer; } + } + + public string Product + { + get { return HubProduct; } + } + + public string SerialNumber + { + get { return HubSerialNumber; } + } + + // return a list of the down stream ports + public System.Collections.ObjectModel.ReadOnlyCollection GetPorts() + { + List PortList = new List(); + + // Open a handle to the Hub device + IntPtr h = CreateFile(HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + int nBytes = Marshal.SizeOf(typeof(USB_NODE_CONNECTION_INFORMATION_EX)); + IntPtr ptrNodeConnection = Marshal.AllocHGlobal(nBytes); + + // loop thru all of the ports on the hub + // BTW: Ports are numbered starting at 1 + for(int i = 1; i <= HubPortCount; i++) + { + int nBytesReturned; + USB_NODE_CONNECTION_INFORMATION_EX NodeConnection = new USB_NODE_CONNECTION_INFORMATION_EX(); + NodeConnection.ConnectionIndex = i; + Marshal.StructureToPtr(NodeConnection, ptrNodeConnection, true); + + if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, ptrNodeConnection, nBytes, + ptrNodeConnection, nBytes, out nBytesReturned, IntPtr.Zero)) + { + NodeConnection = + (USB_NODE_CONNECTION_INFORMATION_EX)Marshal.PtrToStructure(ptrNodeConnection, + typeof(USB_NODE_CONNECTION_INFORMATION_EX)); + + // load up the USBPort class + USBPort port = new USBPort(); + port.PortPortNumber = i; + port.PortHubDevicePath = HubDevicePath; + USB_CONNECTION_STATUS Status = (USB_CONNECTION_STATUS)NodeConnection.ConnectionStatus; + port.PortStatus = Status.ToString(); + USB_DEVICE_SPEED Speed = (USB_DEVICE_SPEED)NodeConnection.Speed; + port.PortSpeed = Speed.ToString(); + port.PortIsDeviceConnected = + (NodeConnection.ConnectionStatus == (int)USB_CONNECTION_STATUS.DeviceConnected); + port.PortIsHub = Convert.ToBoolean(NodeConnection.DeviceIsHub); + port.PortDeviceDescriptor = NodeConnection.DeviceDescriptor; + + // add it to the list + PortList.Add(port); + } + } + Marshal.FreeHGlobal(ptrNodeConnection); + CloseHandle(h); + } + // convert it into a Collection + return new System.Collections.ObjectModel.ReadOnlyCollection(PortList); + } + } + + // + // The Port Class + // + public class USBPort + { + internal int PortPortNumber; + internal string PortStatus, PortHubDevicePath, PortSpeed; + internal bool PortIsHub, PortIsDeviceConnected; + internal USB_DEVICE_DESCRIPTOR PortDeviceDescriptor; + + // a simple default constructor + public USBPort() + { + PortPortNumber = 0; + PortStatus = ""; + PortHubDevicePath = ""; + PortSpeed = ""; + PortIsHub = false; + PortIsDeviceConnected = false; + } + + // return Port Index of the Hub + public int PortNumber + { + get { return PortPortNumber; } + } + + // return the Device Path of the Hub + public string HubDevicePath + { + get { return PortHubDevicePath; } + } + + // the status (see USB_CONNECTION_STATUS above) + public string Status + { + get { return PortStatus; } + } + + // the speed of the connection (see USB_DEVICE_SPEED above) + public string Speed + { + get { return PortSpeed; } + } + + // is this a downstream external hub? + public bool IsHub + { + get { return PortIsHub; } + } + + // is anybody home? + public bool IsDeviceConnected + { + get { return PortIsDeviceConnected; } + } + + // return a down stream external hub + public USBDevice GetDevice() + { + if(!PortIsDeviceConnected) + { + return null; + } + USBDevice Device = new USBDevice(); + + // Copy over some values from the Port class + // Ya know, I've given some thought about making Device a derived class... + Device.DevicePortNumber = PortPortNumber; + Device.DeviceHubDevicePath = PortHubDevicePath; + Device.DeviceDescriptor = PortDeviceDescriptor; + + // Open a handle to the Hub device + IntPtr h = CreateFile(PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + int nBytesReturned; + int nBytes = BUFFER_SIZE; + // We use this to zero fill a buffer + string NullString = new string((char)0, BUFFER_SIZE / Marshal.SystemDefaultCharSize); + + // The iManufacturer, iProduct and iSerialNumber entries in the + // Device Descriptor are really just indexes. So, we have to + // request a String Descriptor to get the values for those strings. + + if(PortDeviceDescriptor.iManufacturer > 0) + { + // build a request for string descriptor + USB_DESCRIPTOR_REQUEST Request = new USB_DESCRIPTOR_REQUEST(); + Request.ConnectionIndex = PortPortNumber; + Request.SetupPacket.wValue = + (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iManufacturer); + Request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(Request)); + Request.SetupPacket.wIndex = 0x409; // Language Code + // Geez, I wish C# had a Marshal.MemSet() method + IntPtr ptrRequest = Marshal.StringToHGlobalAuto(NullString); + Marshal.StructureToPtr(Request, ptrRequest, true); + + // Use an IOCTL call to request the String Descriptor + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, + ptrRequest, nBytes, out nBytesReturned, IntPtr.Zero)) + { + // The location of the string descriptor is immediately after + // the Request structure. Because this location is not "covered" + // by the structure allocation, we're forced to zero out this + // chunk of memory by using the StringToHGlobalAuto() hack above + IntPtr ptrStringDesc = new IntPtr(ptrRequest.ToInt32() + Marshal.SizeOf(Request)); + USB_STRING_DESCRIPTOR StringDesc = + (USB_STRING_DESCRIPTOR)Marshal.PtrToStructure(ptrStringDesc, + typeof(USB_STRING_DESCRIPTOR)); + Device.DeviceManufacturer = StringDesc.bString; + } + Marshal.FreeHGlobal(ptrRequest); + } + if(PortDeviceDescriptor.iProduct > 0) + { + // build a request for string descriptor + USB_DESCRIPTOR_REQUEST Request = new USB_DESCRIPTOR_REQUEST(); + Request.ConnectionIndex = PortPortNumber; + Request.SetupPacket.wValue = + (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iProduct); + Request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(Request)); + Request.SetupPacket.wIndex = 0x409; // Language Code + // Geez, I wish C# had a Marshal.MemSet() method + IntPtr ptrRequest = Marshal.StringToHGlobalAuto(NullString); + Marshal.StructureToPtr(Request, ptrRequest, true); + + // Use an IOCTL call to request the String Descriptor + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, + ptrRequest, nBytes, out nBytesReturned, IntPtr.Zero)) + { + // the location of the string descriptor is immediately after the Request structure + IntPtr ptrStringDesc = new IntPtr(ptrRequest.ToInt32() + Marshal.SizeOf(Request)); + USB_STRING_DESCRIPTOR StringDesc = + (USB_STRING_DESCRIPTOR)Marshal.PtrToStructure(ptrStringDesc, + typeof(USB_STRING_DESCRIPTOR)); + Device.DeviceProduct = StringDesc.bString; + } + Marshal.FreeHGlobal(ptrRequest); + } + if(PortDeviceDescriptor.iSerialNumber > 0) + { + // build a request for string descriptor + USB_DESCRIPTOR_REQUEST Request = new USB_DESCRIPTOR_REQUEST(); + Request.ConnectionIndex = PortPortNumber; + Request.SetupPacket.wValue = + (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iSerialNumber); + Request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(Request)); + Request.SetupPacket.wIndex = 0x409; // Language Code + // Geez, I wish C# had a Marshal.MemSet() method + IntPtr ptrRequest = Marshal.StringToHGlobalAuto(NullString); + Marshal.StructureToPtr(Request, ptrRequest, true); + + // Use an IOCTL call to request the String Descriptor + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, + ptrRequest, nBytes, out nBytesReturned, IntPtr.Zero)) + { + // the location of the string descriptor is immediately after the Request structure + IntPtr ptrStringDesc = new IntPtr(ptrRequest.ToInt32() + Marshal.SizeOf(Request)); + USB_STRING_DESCRIPTOR StringDesc = + (USB_STRING_DESCRIPTOR)Marshal.PtrToStructure(ptrStringDesc, + typeof(USB_STRING_DESCRIPTOR)); + Device.DeviceSerialNumber = StringDesc.bString; + } + Marshal.FreeHGlobal(ptrRequest); + } + + // Get the Driver Key Name (usefull in locating a device) + USB_NODE_CONNECTION_DRIVERKEY_NAME DriverKey = new USB_NODE_CONNECTION_DRIVERKEY_NAME(); + DriverKey.ConnectionIndex = PortPortNumber; + nBytes = Marshal.SizeOf(DriverKey); + IntPtr ptrDriverKey = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(DriverKey, ptrDriverKey, true); + + // Use an IOCTL call to request the Driver Key Name + if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, ptrDriverKey, nBytes, + ptrDriverKey, nBytes, out nBytesReturned, IntPtr.Zero)) + { + DriverKey = (USB_NODE_CONNECTION_DRIVERKEY_NAME)Marshal.PtrToStructure(ptrDriverKey, + typeof(USB_NODE_CONNECTION_DRIVERKEY_NAME)); + Device.DeviceDriverKey = DriverKey.DriverKeyName; + + // use the DriverKeyName to get the Device Description and Instance ID + Device.DeviceName = GetDescriptionByKeyName(Device.DeviceDriverKey); + Device.DeviceInstanceID = GetInstanceIDByKeyName(Device.DeviceDriverKey); + } + Marshal.FreeHGlobal(ptrDriverKey); + CloseHandle(h); + } + return Device; + } + + // return a down stream external hub + public USBHub GetHub() + { + if(!PortIsHub) + { + return null; + } + USBHub Hub = new USBHub(); + IntPtr h, h2; + Hub.HubIsRootHub = false; + Hub.HubDeviceDesc = "External Hub"; + + // Open a handle to the Host Controller + h = CreateFile(PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + // Get the DevicePath for downstream hub + int nBytesReturned; + USB_NODE_CONNECTION_NAME NodeName = new USB_NODE_CONNECTION_NAME(); + NodeName.ConnectionIndex = PortPortNumber; + int nBytes = Marshal.SizeOf(NodeName); + IntPtr ptrNodeName = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(NodeName, ptrNodeName, true); + + // Use an IOCTL call to request the Node Name + if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_NAME, ptrNodeName, nBytes, ptrNodeName, nBytes, + out nBytesReturned, IntPtr.Zero)) + { + NodeName = (USB_NODE_CONNECTION_NAME)Marshal.PtrToStructure(ptrNodeName, + typeof(USB_NODE_CONNECTION_NAME)); + Hub.HubDevicePath = @"\\.\" + NodeName.NodeName; + } + + // Now let's open the Hub (based upon the HubName we got above) + h2 = CreateFile(Hub.HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + if(h2.ToInt32() != INVALID_HANDLE_VALUE) + { + USB_NODE_INFORMATION NodeInfo = new USB_NODE_INFORMATION(); + NodeInfo.NodeType = (int)USB_HUB_NODE.UsbHub; + nBytes = Marshal.SizeOf(NodeInfo); + IntPtr ptrNodeInfo = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(NodeInfo, ptrNodeInfo, true); + + // get the Hub Information + if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, ptrNodeInfo, nBytes, ptrNodeInfo, nBytes, + out nBytesReturned, IntPtr.Zero)) + { + NodeInfo = (USB_NODE_INFORMATION)Marshal.PtrToStructure(ptrNodeInfo, + typeof(USB_NODE_INFORMATION)); + Hub.HubIsBusPowered = Convert.ToBoolean(NodeInfo.HubInformation.HubIsBusPowered); + Hub.HubPortCount = NodeInfo.HubInformation.HubDescriptor.bNumberOfPorts; + } + Marshal.FreeHGlobal(ptrNodeInfo); + CloseHandle(h2); + } + + // Fill in the missing Manufacture, Product, and SerialNumber values + // values by just creating a Device instance and copying the values + USBDevice Device = GetDevice(); + Hub.HubInstanceID = Device.DeviceInstanceID; + Hub.HubManufacturer = Device.Manufacturer; + Hub.HubProduct = Device.Product; + Hub.HubSerialNumber = Device.SerialNumber; + Hub.HubDriverKey = Device.DriverKey; + + Marshal.FreeHGlobal(ptrNodeName); + CloseHandle(h); + } + return Hub; + } + } + + // + // The USB Device Class + // + public class USBDevice + { + internal int DevicePortNumber; + internal string DeviceDriverKey, DeviceHubDevicePath, DeviceInstanceID, DeviceName; + internal string DeviceManufacturer, DeviceProduct, DeviceSerialNumber; + internal USB_DEVICE_DESCRIPTOR DeviceDescriptor; + + // a simple default constructor + public USBDevice() + { + DevicePortNumber = 0; + DeviceHubDevicePath = ""; + DeviceDriverKey = ""; + DeviceManufacturer = ""; + DeviceProduct = "Unknown USB Device"; + DeviceSerialNumber = ""; + DeviceName = ""; + DeviceInstanceID = ""; + } + + // return Port Index of the Hub + public int PortNumber + { + get { return DevicePortNumber; } + } + + // return the Device Path of the Hub (the parent device) + public string HubDevicePath + { + get { return DeviceHubDevicePath; } + } + + // useful as a search key + public string DriverKey + { + get { return DeviceDriverKey; } + } + + // the device path of this device + public string InstanceID + { + get { return DeviceInstanceID; } + } + + // the friendly name + public string Name + { + get { return DeviceName; } + } + + public string Manufacturer + { + get { return DeviceManufacturer; } + } + + public string Product + { + get { return DeviceProduct; } + } + + public string SerialNumber + { + get { return DeviceSerialNumber; } + } + } + + // + // private function for finding a USB device's Description + // + static string GetDescriptionByKeyName(string DriverKeyName) + { + string ans = ""; + string DevEnum = REGSTR_KEY_USB; + + // Use the "enumerator form" of the SetupDiGetClassDevs API + // to generate a list of all USB devices + IntPtr h = SetupDiGetClassDevs(0, DevEnum, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); + string KeyName; + + bool Success; + int i = 0; + do + { + // create a Device Interface Data structure + SP_DEVINFO_DATA da = new SP_DEVINFO_DATA(); + da.cbSize = Marshal.SizeOf(da); + + // start the enumeration + Success = SetupDiEnumDeviceInfo(h, i, ref da); + if(Success) + { + int RequiredSize = 0; + int RegType = REG_SZ; + KeyName = ""; + + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref RegType, ptrBuf, BUFFER_SIZE, + ref RequiredSize)) + { + KeyName = Marshal.PtrToStringAuto(ptrBuf); + } + + // is it a match? + if(KeyName == DriverKeyName) + { + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DEVICEDESC, ref RegType, ptrBuf, + BUFFER_SIZE, ref RequiredSize)) + { + ans = Marshal.PtrToStringAuto(ptrBuf); + } + break; + } + } + i++; + } while(Success); + + Marshal.FreeHGlobal(ptrBuf); + SetupDiDestroyDeviceInfoList(h); + } + return ans; + } + + // + // private function for finding a USB device's Instance ID + // + static string GetInstanceIDByKeyName(string DriverKeyName) + { + string ans = ""; + string DevEnum = REGSTR_KEY_USB; + + // Use the "enumerator form" of the SetupDiGetClassDevs API + // to generate a list of all USB devices + IntPtr h = SetupDiGetClassDevs(0, DevEnum, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); + string KeyName; + + bool Success; + int i = 0; + do + { + // create a Device Interface Data structure + SP_DEVINFO_DATA da = new SP_DEVINFO_DATA(); + da.cbSize = Marshal.SizeOf(da); + + // start the enumeration + Success = SetupDiEnumDeviceInfo(h, i, ref da); + if(Success) + { + int RequiredSize = 0; + int RegType = REG_SZ; + + KeyName = ""; + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref RegType, ptrBuf, BUFFER_SIZE, + ref RequiredSize)) + { + KeyName = Marshal.PtrToStringAuto(ptrBuf); + } + + // is it a match? + if(KeyName == DriverKeyName) + { + int nBytes = BUFFER_SIZE; + StringBuilder sb = new StringBuilder(nBytes); + SetupDiGetDeviceInstanceId(h, ref da, sb, nBytes, out RequiredSize); + ans = sb.ToString(); + break; + } + } + i++; + } while(Success); + + Marshal.FreeHGlobal(ptrBuf); + SetupDiDestroyDeviceInfoList(h); + } + return ans; + } + } +} diff --git a/DiscImageChef.Devices/Windows/UsbFunctions.cs b/DiscImageChef.Devices/Windows/UsbFunctions.cs new file mode 100644 index 00000000..0c7105f4 --- /dev/null +++ b/DiscImageChef.Devices/Windows/UsbFunctions.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; + +// Copyright "Fort Hood TX", public domain, 2007 +namespace DiscImageChef.Devices.Windows +{ + + // + // A place for "higher level" related functions + // You might not want to keep these in the USB class... your choice + // + public partial class Usb + { + + // + // Get a list of all connected devices + // + static public List GetConnectedDevices() + { + List DevList = new List(); + + foreach(USBController Controller in GetHostControllers()) + { + ListHub(Controller.GetRootHub(), DevList); + } + return DevList; + } + + // private routine for enumerating a hub + static void ListHub(USBHub Hub, List DevList) + { + foreach(USBPort Port in Hub.GetPorts()) + { + if(Port.IsHub) + { + // recursive + ListHub(Port.GetHub(), DevList); + } + else + { + if(Port.IsDeviceConnected) + { + DevList.Add(Port.GetDevice()); + } + } + } + } + + // + // Find a device based upon it's DriverKeyName + // + static public USBDevice FindDeviceByDriverKeyName(string DriverKeyName) + { + USBDevice FoundDevice = null; + + foreach(USBController Controller in GetHostControllers()) + { + SearchHubDriverKeyName(Controller.GetRootHub(), ref FoundDevice, DriverKeyName); + if(FoundDevice != null) + break; + } + return FoundDevice; + } + + // private routine for enumerating a hub + static void SearchHubDriverKeyName(USBHub Hub, ref USBDevice FoundDevice, string DriverKeyName) + { + foreach(USBPort Port in Hub.GetPorts()) + { + if(Port.IsHub) + { + // recursive + SearchHubDriverKeyName(Port.GetHub(), ref FoundDevice, DriverKeyName); + } + else + { + if(Port.IsDeviceConnected) + { + USBDevice Device = Port.GetDevice(); + if(Device.DeviceDriverKey == DriverKeyName) + { + FoundDevice = Device; + break; + } + } + } + } + } + + // + // Find a device based upon it's Instance ID + // + static public USBDevice FindDeviceByInstanceID(string InstanceID) + { + USBDevice FoundDevice = null; + + foreach(USBController Controller in GetHostControllers()) + { + SearchHubInstanceID(Controller.GetRootHub(), ref FoundDevice, InstanceID); + if(FoundDevice != null) + break; + } + return FoundDevice; + } + + // private routine for enumerating a hub + static void SearchHubInstanceID(USBHub Hub, ref USBDevice FoundDevice, string InstanceID) + { + foreach(USBPort Port in Hub.GetPorts()) + { + if(Port.IsHub) + { + // recursive + SearchHubInstanceID(Port.GetHub(), ref FoundDevice, InstanceID); + } + else + { + if(Port.IsDeviceConnected) + { + USBDevice Device = Port.GetDevice(); + if(Device.InstanceID == InstanceID) + { + FoundDevice = Device; + break; + } + } + } + } + } + + const int IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080; + public const string GUID_DEVINTERFACE_DISK = "53f56307-b6bf-11d0-94f2-00a0c91efb8b"; + public const string GUID_DEVINTERFACE_CDROM = "53f56308-b6bf-11d0-94f2-00a0c91efb8b"; + public const string GUID_DEVINTERFACE_FLOPPY = "53f56311-b6bf-11d0-94f2-00a0c91efb8b"; + + //typedef struct _STORAGE_DEVICE_NUMBER { + // DEVICE_TYPE DeviceType; + // ULONG DeviceNumber; + // ULONG PartitionNumber; + //} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; + [StructLayout(LayoutKind.Sequential)] + struct STORAGE_DEVICE_NUMBER + { + public int DeviceType; + public int DeviceNumber; + public int PartitionNumber; + } + + //CMAPI CONFIGRET WINAPI CM_Get_Parent( + // OUT PDEVINST pdnDevInst, + // IN DEVINST dnDevInst, + // IN ULONG ulFlags + //); + [DllImport("setupapi.dll")] + static extern int CM_Get_Parent( + out IntPtr pdnDevInst, + IntPtr dnDevInst, + int ulFlags + ); + + //CMAPI CONFIGRET WINAPI CM_Get_Device_ID( + // IN DEVINST dnDevInst, + // OUT PTCHAR Buffer, + // IN ULONG BufferLen, + // IN ULONG ulFlags + //); + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static extern int CM_Get_Device_ID( + IntPtr dnDevInst, + IntPtr Buffer, + int BufferLen, + int ulFlags + ); + + // + // Find a device based upon a Drive Letter + // + static public USBDevice FindDriveLetter(string DriveLetter, string deviceGuid) + { + USBDevice FoundDevice = null; + string InstanceID = ""; + + // We start by getting the unique DeviceNumber of the given + // DriveLetter. We'll use this later to find a matching + // DevicePath "symbolic name" + int DevNum = GetDeviceNumber(@"\\.\" + DriveLetter.TrimEnd('\\')); + if(DevNum < 0) + { + return null; + } + + return FindDeviceNumber(DevNum, deviceGuid); + } + + static public USBDevice FindDrivePath(string DrivePath, string deviceGuid) + { + USBDevice FoundDevice = null; + string InstanceID = ""; + + // We start by getting the unique DeviceNumber of the given + // DriveLetter. We'll use this later to find a matching + // DevicePath "symbolic name" + int DevNum = GetDeviceNumber(DrivePath); + if(DevNum < 0) + { + return null; + } + + return FindDeviceNumber(DevNum, deviceGuid); + } + + // + // Find a device based upon a Drive Letter + // + static public USBDevice FindDeviceNumber(int DevNum, string deviceGuid) + { + USBDevice FoundDevice = null; + string InstanceID = ""; + + Guid DiskGUID = new Guid(deviceGuid); + + // We start at the "root" of the device tree and look for all + // devices that match the interface GUID of a disk + IntPtr h = SetupDiGetClassDevs(ref DiskGUID, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + bool Success = true; + int i = 0; + do + { + // create a Device Interface Data structure + SP_DEVICE_INTERFACE_DATA dia = new SP_DEVICE_INTERFACE_DATA(); + dia.cbSize = Marshal.SizeOf(dia); + + // start the enumeration + Success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref DiskGUID, i, ref dia); + if(Success) + { + // build a DevInfo Data structure + SP_DEVINFO_DATA da = new SP_DEVINFO_DATA(); + da.cbSize = Marshal.SizeOf(da); + + // build a Device Interface Detail Data structure + SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA(); + didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // trust me :) + + // now we can get some more detailed information + int nRequiredSize = 0; + int nBytes = BUFFER_SIZE; + if(SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, nBytes, ref nRequiredSize, ref da)) + { + // Now that we have a DevicePath... we can use it to + // generate another DeviceNumber to see if it matches + // the one we're looking for. + if(GetDeviceNumber(didd.DevicePath) == DevNum) + { + // current InstanceID is at the "USBSTOR" level, so we + // need up "move up" one level to get to the "USB" level + IntPtr ptrPrevious; + CM_Get_Parent(out ptrPrevious, da.DevInst, 0); + + // Now we get the InstanceID of the USB level device + IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes); + CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, nBytes, 0); + InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf); + + Marshal.FreeHGlobal(ptrInstanceBuf); + System.Console.WriteLine("InstanceId: {0}", InstanceID); + //break; + } + } + } + i++; + } while(Success); + SetupDiDestroyDeviceInfoList(h); + } + + // Did we find an InterfaceID of a USB device? + if(InstanceID.StartsWith("USB\\")) + { + FoundDevice = FindDeviceByInstanceID(InstanceID); + } + return FoundDevice; + } + + // return a unique device number for the given device path + private static int GetDeviceNumber(string DevicePath) + { + int ans = -1; + + IntPtr h = CreateFile(DevicePath.TrimEnd('\\'), 0, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); + if(h.ToInt32() != INVALID_HANDLE_VALUE) + { + int requiredSize; + STORAGE_DEVICE_NUMBER Sdn = new STORAGE_DEVICE_NUMBER(); + int nBytes = Marshal.SizeOf(Sdn); + IntPtr ptrSdn = Marshal.AllocHGlobal(nBytes); + + if(DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ptrSdn, nBytes, out requiredSize, + IntPtr.Zero)) + { + Sdn = (STORAGE_DEVICE_NUMBER)Marshal.PtrToStructure(ptrSdn, typeof(STORAGE_DEVICE_NUMBER)); + // just my way of combining the relevant parts of the + // STORAGE_DEVICE_NUMBER into a single number + ans = (Sdn.DeviceType << 8) + Sdn.DeviceNumber; + } + Marshal.FreeHGlobal(ptrSdn); + CloseHandle(h); + } + return ans; + } + } +}