Consistent naming fixes in our usage tables enumerations.
2 using System.Windows.Forms;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
6 using Microsoft.Win32.SafeHandles;
12 /// Represent a HID device.
13 /// Rename to RawInputDevice?
15 public class HidDevice: IDisposable
18 /// Unique name of that HID device.
19 /// Notably used as input to CreateFile.
21 public string Name { get; private set; }
24 /// Friendly name that people should be able to read.
26 public string FriendlyName { get; private set; }
29 public string Manufacturer { get; private set; }
30 public string Product { get; private set; }
31 public ushort VendorId { get; private set; }
32 public ushort ProductId { get; private set; }
33 public ushort Version { get; private set; }
34 //Pre-parsed HID descriptor
35 public IntPtr PreParsedData {get; private set;}
37 public RID_DEVICE_INFO Info { get {return iInfo;} }
38 private RID_DEVICE_INFO iInfo;
40 public HIDP_CAPS Capabilities { get { return iCapabilities; } }
41 private HIDP_CAPS iCapabilities;
42 public string InputCapabilitiesDescription { get; private set; }
43 //Input Button Capabilities
44 public HIDP_BUTTON_CAPS[] InputButtonCapabilities { get { return iInputButtonCapabilities; } }
45 private HIDP_BUTTON_CAPS[] iInputButtonCapabilities;
46 //Input Value Capabilities
47 public HIDP_VALUE_CAPS[] InputValueCapabilities { get { return iInputValueCapabilities; } }
48 private HIDP_VALUE_CAPS[] iInputValueCapabilities;
51 /// Class constructor will fetch this object properties from HID sub system.
53 /// <param name="hRawInputDevice">Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice</param>
54 public HidDevice(IntPtr hRawInputDevice)
56 //Try construct and rollback if needed
59 Construct(hRawInputDevice);
61 catch (System.Exception ex)
63 //Just rollback and propagate
71 /// Make sure dispose is called even if the user forgot about it.
79 /// Private constructor.
81 /// <param name="hRawInputDevice"></param>
82 private void Construct(IntPtr hRawInputDevice)
84 PreParsedData = IntPtr.Zero;
85 iInputButtonCapabilities = null;
86 iInputValueCapabilities = null;
88 //Fetch various information defining the given HID device
89 Name = Win32.RawInput.GetDeviceName(hRawInputDevice);
92 iInfo = new RID_DEVICE_INFO();
93 if (!Win32.RawInput.GetDeviceInfo(hRawInputDevice, ref iInfo))
95 throw new Exception("HidDevice: GetDeviceInfo failed: " + Marshal.GetLastWin32Error().ToString());
98 //Open our device from the device name/path
99 SafeFileHandle handle = Win32.Function.CreateFile(Name,
100 Win32.FileAccess.NONE,
101 Win32.FileShare.FILE_SHARE_READ | Win32.FileShare.FILE_SHARE_WRITE,
103 Win32.CreationDisposition.OPEN_EXISTING,
104 Win32.FileFlagsAttributes.FILE_FLAG_OVERLAPPED,
108 //Check if CreateFile worked
109 if (handle.IsInvalid)
111 throw new Exception("HidDevice: CreateFile failed: " + Marshal.GetLastWin32Error().ToString());
114 //Get manufacturer string
115 StringBuilder manufacturerString = new StringBuilder(256);
116 if (Win32.Function.HidD_GetManufacturerString(handle, manufacturerString, manufacturerString.Capacity))
118 Manufacturer = manufacturerString.ToString();
122 StringBuilder productString = new StringBuilder(256);
123 if (Win32.Function.HidD_GetProductString(handle, productString, productString.Capacity))
125 Product = productString.ToString();
129 Win32.HIDD_ATTRIBUTES attributes = new Win32.HIDD_ATTRIBUTES();
130 if (Win32.Function.HidD_GetAttributes(handle, ref attributes))
132 VendorId = attributes.VendorID;
133 ProductId = attributes.ProductID;
134 Version = attributes.VersionNumber;
141 //Get our HID descriptor pre-parsed data
142 PreParsedData = Win32.RawInput.GetPreParsedData(hRawInputDevice);
144 if (PreParsedData == IntPtr.Zero)
147 //Some devices don't have pre-parsed data.
152 HidStatus status = Win32.Function.HidP_GetCaps(PreParsedData, ref iCapabilities);
153 if (status != HidStatus.HIDP_STATUS_SUCCESS)
155 throw new Exception("HidDevice: HidP_GetCaps failed: " + status.ToString());
158 SetInputCapabilitiesDescription();
160 //Get input button caps if needed
161 if (Capabilities.NumberInputButtonCaps > 0)
163 iInputButtonCapabilities = new HIDP_BUTTON_CAPS[Capabilities.NumberInputButtonCaps];
164 ushort buttonCapabilitiesLength = Capabilities.NumberInputButtonCaps;
165 status = Win32.Function.HidP_GetButtonCaps(HIDP_REPORT_TYPE.HidP_Input, iInputButtonCapabilities, ref buttonCapabilitiesLength, PreParsedData);
166 if (status != HidStatus.HIDP_STATUS_SUCCESS || buttonCapabilitiesLength != Capabilities.NumberInputButtonCaps)
168 throw new Exception("HidDevice: HidP_GetButtonCaps failed: " + status.ToString());
172 //Get input value caps if needed
173 if (Capabilities.NumberInputValueCaps > 0)
175 iInputValueCapabilities = new HIDP_VALUE_CAPS[Capabilities.NumberInputValueCaps];
176 ushort valueCapabilitiesLength = Capabilities.NumberInputValueCaps;
177 status = Win32.Function.HidP_GetValueCaps(HIDP_REPORT_TYPE.HidP_Input, iInputValueCapabilities, ref valueCapabilitiesLength, PreParsedData);
178 if (status != HidStatus.HIDP_STATUS_SUCCESS || valueCapabilitiesLength != Capabilities.NumberInputValueCaps)
180 throw new Exception("HidDevice: HidP_GetValueCaps failed: " + status.ToString());
188 void SetInputCapabilitiesDescription()
190 InputCapabilitiesDescription = "[ Input Capabilities ] Button: " + Capabilities.NumberInputButtonCaps + " - Value: " + Capabilities.NumberInputValueCaps + " - Data indices: " + Capabilities.NumberInputDataIndices;
196 private void SetFriendlyName()
198 //Work out proper suffix for our device root node.
199 //That allows users to see in a glance what kind of device this is.
201 Type usageCollectionType = null;
202 if (Info.dwType == RawInputDeviceType.RIM_TYPEHID)
205 if (Enum.IsDefined(typeof(Hid.UsagePage), Info.hid.usUsagePage))
207 //We know this usage page, add its name
208 Hid.UsagePage usagePage = (Hid.UsagePage)Info.hid.usUsagePage;
209 suffix += " ( " + usagePage.ToString() + ", ";
210 usageCollectionType = Hid.Utils.UsageCollectionType(usagePage);
214 //We don't know this usage page, add its value
215 suffix += " ( 0x" + Info.hid.usUsagePage.ToString("X4") + ", ";
218 //Process usage collection
219 //We don't know this usage page, add its value
220 if (usageCollectionType == null || !Enum.IsDefined(usageCollectionType, Info.hid.usUsage))
223 suffix += "0x" + Info.hid.usUsage.ToString("X4") + " )";
227 //We know this usage page, add its name
228 suffix += Enum.GetName(usageCollectionType, Info.hid.usUsage) + " )";
231 else if (Info.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD)
233 suffix = " - Keyboard";
235 else if (Info.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
240 if (Product != null && Product.Length > 1)
242 //Add the devices with a proper name at the top
243 FriendlyName = Product + suffix;
247 //Add other once at the bottom
248 FriendlyName = "0x" + ProductId.ToString("X4") + suffix;
254 /// Dispose is just for unmanaged clean-up.
255 /// Make sure calling disposed multiple times does not crash.
256 /// See: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface/538238#538238
258 public void Dispose()
260 Marshal.FreeHGlobal(PreParsedData);
261 PreParsedData = IntPtr.Zero;
267 /// <param name="aCaps"></param>
268 /// <returns></returns>
269 public string InputValueCapabilityDescription(HIDP_VALUE_CAPS aCaps)
271 if (!aCaps.IsRange && Enum.IsDefined(typeof(UsagePage), Capabilities.UsagePage))
273 return "Input Value: " + Enum.GetName(Utils.UsageType((UsagePage)Capabilities.UsagePage), aCaps.NotRange.Usage);
279 public bool IsGamePad
283 return ((UsagePage)iCapabilities.UsagePage == UsagePage.GenericDesktopControls && (UsageCollection.GenericDesktop)iCapabilities.Usage == UsageCollection.GenericDesktop.GamePad);
289 /// Print information about this device to our debug output.
291 public void DebugWrite()
293 Debug.WriteLine("================ HID =========================================================================================");
294 Debug.WriteLine("==== Name: " + Name);
295 Debug.WriteLine("==== Manufacturer: " + Manufacturer);
296 Debug.WriteLine("==== Product: " + Product);
297 Debug.WriteLine("==== VendorID: 0x" + VendorId.ToString("X4"));
298 Debug.WriteLine("==== ProductID: 0x" + ProductId.ToString("X4"));
299 Debug.WriteLine("==== Version: " + Version.ToString());
300 Debug.WriteLine("==============================================================================================================");