HidDevice: Adding friendly name property.
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 //Input Button Capabilities
43 public HIDP_BUTTON_CAPS[] InputButtonCapabilities { get { return iInputButtonCapabilities; } }
44 private HIDP_BUTTON_CAPS[] iInputButtonCapabilities;
45 //Input Value Capabilities
46 public HIDP_VALUE_CAPS[] InputValueCapabilities { get { return iInputValueCapabilities; } }
47 private HIDP_VALUE_CAPS[] iInputValueCapabilities;
53 /// Class constructor will fetch this object properties from HID sub system.
55 /// <param name="hRawInputDevice">Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice</param>
56 public HidDevice(IntPtr hRawInputDevice)
58 //Try construct and rollback if needed
61 Construct(hRawInputDevice);
63 catch (System.Exception ex)
65 //Just rollback and propagate
73 /// Make sure dispose is called even if the user forgot about it.
81 /// Private constructor.
83 /// <param name="hRawInputDevice"></param>
84 private void Construct(IntPtr hRawInputDevice)
86 PreParsedData = IntPtr.Zero;
87 iInputButtonCapabilities = null;
88 iInputValueCapabilities = null;
90 //Fetch various information defining the given HID device
91 Name = Win32.RawInput.GetDeviceName(hRawInputDevice);
94 iInfo = new RID_DEVICE_INFO();
95 if (!Win32.RawInput.GetDeviceInfo(hRawInputDevice, ref iInfo))
97 throw new Exception("HidDevice: GetDeviceInfo failed: " + Marshal.GetLastWin32Error().ToString());
100 //Open our device from the device name/path
101 SafeFileHandle handle = Win32.Function.CreateFile(Name,
102 Win32.FileAccess.NONE,
103 Win32.FileShare.FILE_SHARE_READ | Win32.FileShare.FILE_SHARE_WRITE,
105 Win32.CreationDisposition.OPEN_EXISTING,
106 Win32.FileFlagsAttributes.FILE_FLAG_OVERLAPPED,
110 //Check if CreateFile worked
111 if (handle.IsInvalid)
113 throw new Exception("HidDevice: CreateFile failed: " + Marshal.GetLastWin32Error().ToString());
116 //Get manufacturer string
117 StringBuilder manufacturerString = new StringBuilder(256);
118 if (Win32.Function.HidD_GetManufacturerString(handle, manufacturerString, manufacturerString.Capacity))
120 Manufacturer = manufacturerString.ToString();
124 StringBuilder productString = new StringBuilder(256);
125 if (Win32.Function.HidD_GetProductString(handle, productString, productString.Capacity))
127 Product = productString.ToString();
131 Win32.HIDD_ATTRIBUTES attributes = new Win32.HIDD_ATTRIBUTES();
132 if (Win32.Function.HidD_GetAttributes(handle, ref attributes))
134 VendorId = attributes.VendorID;
135 ProductId = attributes.ProductID;
136 Version = attributes.VersionNumber;
143 //Get our HID descriptor pre-parsed data
144 PreParsedData = Win32.RawInput.GetPreParsedData(hRawInputDevice);
146 if (PreParsedData == IntPtr.Zero)
149 //Some devices don't have pre-parsed data.
154 HidStatus status = Win32.Function.HidP_GetCaps(PreParsedData, ref iCapabilities);
155 if (status != HidStatus.HIDP_STATUS_SUCCESS)
157 throw new Exception("HidDevice: HidP_GetCaps failed: " + status.ToString());
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());
192 private void SetFriendlyName()
194 //Work out proper suffix for our device root node.
195 //That allows users to see in a glance what kind of device this is.
197 Type usageCollectionType = null;
198 if (Info.dwType == RawInputDeviceType.RIM_TYPEHID)
201 if (Enum.IsDefined(typeof(Hid.UsagePage), Info.hid.usUsagePage))
203 //We know this usage page, add its name
204 Hid.UsagePage usagePage = (Hid.UsagePage)Info.hid.usUsagePage;
205 suffix += " ( " + usagePage.ToString() + ", ";
206 usageCollectionType = Hid.Utils.UsageCollectionType(usagePage);
210 //We don't know this usage page, add its value
211 suffix += " ( 0x" + Info.hid.usUsagePage.ToString("X4") + ", ";
214 //Process usage collection
215 //We don't know this usage page, add its value
216 if (usageCollectionType == null || !Enum.IsDefined(usageCollectionType, Info.hid.usUsage))
219 suffix += "0x" + Info.hid.usUsage.ToString("X4") + " )";
223 //We know this usage page, add its name
224 suffix += Enum.GetName(usageCollectionType, Info.hid.usUsage) + " )";
227 else if (Info.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD)
229 suffix = " - Keyboard";
231 else if (Info.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
236 if (Product != null && Product.Length > 1)
238 //Add the devices with a proper name at the top
239 FriendlyName = Product + suffix;
243 //Add other once at the bottom
244 FriendlyName = "0x" + ProductId.ToString("X4") + suffix;
250 /// Dispose is just for unmanaged clean-up.
251 /// Make sure calling disposed multiple times does not crash.
252 /// See: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface/538238#538238
254 public void Dispose()
256 Marshal.FreeHGlobal(PreParsedData);
257 PreParsedData = IntPtr.Zero;
261 /// Print information about this device to our debug output.
263 public void DebugWrite()
265 Debug.WriteLine("================ HID =========================================================================================");
266 Debug.WriteLine("==== Name: " + Name);
267 Debug.WriteLine("==== Manufacturer: " + Manufacturer);
268 Debug.WriteLine("==== Product: " + Product);
269 Debug.WriteLine("==== VendorID: 0x" + VendorId.ToString("X4"));
270 Debug.WriteLine("==== ProductID: 0x" + ProductId.ToString("X4"));
271 Debug.WriteLine("==== Version: " + Version.ToString());
272 Debug.WriteLine("==============================================================================================================");