using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; namespace DS4Windows { public class HidDevices { private static Guid _hidClassGuid = Guid.Empty; public static bool IsConnected(string devicePath) { return EnumerateDevices().Any(x => x.Path == devicePath); } public static HidDevice GetDevice(string devicePath) { return Enumerate(devicePath).FirstOrDefault(); } public static IEnumerable Enumerate() { return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)); } public static IEnumerable Enumerate(string devicePath) { return EnumerateDevices().Where(x => x.Path == devicePath).Select(x => new HidDevice(x.Path, x.Description)); } public static IEnumerable Enumerate(int vendorId, params int[] productIds) { return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId && productIds.Contains(x.Attributes.ProductId)); } public static IEnumerable Enumerate(int vendorId) { return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId); } private class DeviceInfo { public string Path { get; set; } public string Description { get; set; } } private static IEnumerable EnumerateDevices() { var devices = new List(); var hidClass = HidClassGuid; var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidClass, null, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) { var deviceInfoData = CreateDeviceInfoData(); var deviceIndex = 0; while (NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, ref deviceInfoData)) { deviceIndex += 1; var deviceInterfaceData = new NativeMethods.SP_DEVICE_INTERFACE_DATA(); deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); var deviceInterfaceIndex = 0; while (NativeMethods.SetupDiEnumDeviceInterfaces(deviceInfoSet, ref deviceInfoData, ref hidClass, deviceInterfaceIndex, ref deviceInterfaceData)) { deviceInterfaceIndex++; var devicePath = GetDevicePath(deviceInfoSet, deviceInterfaceData); var description = GetBusReportedDeviceDescription(deviceInfoSet, ref deviceInfoData) ?? GetDeviceDescription(deviceInfoSet, ref deviceInfoData); devices.Add(new DeviceInfo { Path = devicePath, Description = description }); } } NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); } return devices; } private static NativeMethods.SP_DEVINFO_DATA CreateDeviceInfoData() { var deviceInfoData = new NativeMethods.SP_DEVINFO_DATA(); deviceInfoData.cbSize = Marshal.SizeOf(deviceInfoData); deviceInfoData.DevInst = 0; deviceInfoData.ClassGuid = Guid.Empty; deviceInfoData.Reserved = IntPtr.Zero; return deviceInfoData; } private static string GetDevicePath(IntPtr deviceInfoSet, NativeMethods.SP_DEVICE_INTERFACE_DATA deviceInterfaceData) { var bufferSize = 0; var interfaceDetail = new NativeMethods.SP_DEVICE_INTERFACE_DETAIL_DATA { Size = IntPtr.Size == 4 ? 4 + Marshal.SystemDefaultCharSize : 8 }; NativeMethods.SetupDiGetDeviceInterfaceDetailBuffer(deviceInfoSet, ref deviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, IntPtr.Zero); return NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref interfaceDetail, bufferSize, ref bufferSize, IntPtr.Zero) ? interfaceDetail.DevicePath : null; } private static Guid HidClassGuid { get { if (_hidClassGuid.Equals(Guid.Empty)) NativeMethods.HidD_GetHidGuid(ref _hidClassGuid); return _hidClassGuid; } } private static string GetDeviceDescription(IntPtr deviceInfoSet, ref NativeMethods.SP_DEVINFO_DATA devinfoData) { var descriptionBuffer = new byte[1024]; var requiredSize = 0; var type = 0; NativeMethods.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devinfoData, NativeMethods.SPDRP_DEVICEDESC, ref type, descriptionBuffer, descriptionBuffer.Length, ref requiredSize); return descriptionBuffer.ToUTF8String(); } private static string GetBusReportedDeviceDescription(IntPtr deviceInfoSet, ref NativeMethods.SP_DEVINFO_DATA devinfoData) { var descriptionBuffer = new byte[1024]; if (Environment.OSVersion.Version.Major > 5) { ulong propertyType = 0; var requiredSize = 0; var _continue = NativeMethods.SetupDiGetDeviceProperty(deviceInfoSet, ref devinfoData, ref NativeMethods.DEVPKEY_Device_BusReportedDeviceDesc, ref propertyType, descriptionBuffer, descriptionBuffer.Length, ref requiredSize, 0); if (_continue) return descriptionBuffer.ToUTF16String(); } return null; } } }