HidDevice.cs
author StephaneLenclud
Sun, 15 Feb 2015 21:48:07 +0100
changeset 63 4b8b058de215
parent 61 60bfe5083721
child 64 8c2380995bb7
permissions -rw-r--r--
HidDevice: Adding friendly name property.
     1 using System;
     2 using System.Windows.Forms;
     3 using System.Runtime.InteropServices;
     4 using System.Diagnostics;
     5 using System.Text;
     6 using Microsoft.Win32.SafeHandles;
     7 using Win32;
     8 
     9 namespace Hid
    10 {
    11     /// <summary>
    12     /// Represent a HID device.
    13     /// Rename to RawInputDevice?
    14     /// </summary>
    15     public class HidDevice: IDisposable
    16     {
    17         /// <summary>
    18         /// Unique name of that HID device.
    19         /// Notably used as input to CreateFile.
    20         /// </summary>
    21         public string Name { get; private set; }
    22 
    23         /// <summary>
    24         /// Friendly name that people should be able to read.
    25         /// </summary>
    26         public string FriendlyName { get; private set; }
    27 
    28         //
    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;}
    36         //Info
    37         public RID_DEVICE_INFO Info { get {return iInfo;} }
    38         private RID_DEVICE_INFO iInfo;
    39         //Capabilities
    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;
    48 
    49         
    50         
    51 
    52         /// <summary>
    53         /// Class constructor will fetch this object properties from HID sub system.
    54         /// </summary>
    55         /// <param name="hRawInputDevice">Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice</param>
    56         public HidDevice(IntPtr hRawInputDevice)
    57         {
    58             //Try construct and rollback if needed
    59             try
    60             {
    61                 Construct(hRawInputDevice);
    62             }
    63             catch (System.Exception ex)
    64             {
    65                 //Just rollback and propagate
    66                 Dispose();
    67                 throw ex;
    68             }
    69         }
    70 
    71 
    72         /// <summary>
    73         /// Make sure dispose is called even if the user forgot about it.
    74         /// </summary>
    75         ~HidDevice()
    76         {
    77             Dispose();
    78         }
    79 
    80         /// <summary>
    81         /// Private constructor.
    82         /// </summary>
    83         /// <param name="hRawInputDevice"></param>
    84         private void Construct(IntPtr hRawInputDevice)
    85         {
    86             PreParsedData = IntPtr.Zero;
    87             iInputButtonCapabilities = null;
    88             iInputValueCapabilities = null;
    89 
    90             //Fetch various information defining the given HID device
    91             Name = Win32.RawInput.GetDeviceName(hRawInputDevice);
    92 
    93             //Fetch device info
    94             iInfo = new RID_DEVICE_INFO();
    95             if (!Win32.RawInput.GetDeviceInfo(hRawInputDevice, ref iInfo))
    96             {
    97                 throw new Exception("HidDevice: GetDeviceInfo failed: " + Marshal.GetLastWin32Error().ToString());
    98             }
    99 
   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,
   104                 IntPtr.Zero,
   105                 Win32.CreationDisposition.OPEN_EXISTING,
   106                 Win32.FileFlagsAttributes.FILE_FLAG_OVERLAPPED,
   107                 IntPtr.Zero
   108                 );
   109 
   110             //Check if CreateFile worked
   111             if (handle.IsInvalid)
   112             {
   113                 throw new Exception("HidDevice: CreateFile failed: " + Marshal.GetLastWin32Error().ToString());
   114             }
   115 
   116             //Get manufacturer string
   117             StringBuilder manufacturerString = new StringBuilder(256);
   118             if (Win32.Function.HidD_GetManufacturerString(handle, manufacturerString, manufacturerString.Capacity))
   119             {
   120                 Manufacturer = manufacturerString.ToString();
   121             }
   122 
   123             //Get product string
   124             StringBuilder productString = new StringBuilder(256);
   125             if (Win32.Function.HidD_GetProductString(handle, productString, productString.Capacity))
   126             {
   127                 Product = productString.ToString();
   128             }
   129 
   130             //Get attributes
   131             Win32.HIDD_ATTRIBUTES attributes = new Win32.HIDD_ATTRIBUTES();
   132             if (Win32.Function.HidD_GetAttributes(handle, ref attributes))
   133             {
   134                 VendorId = attributes.VendorID;
   135                 ProductId = attributes.ProductID;
   136                 Version = attributes.VersionNumber;
   137             }
   138 
   139             handle.Close();
   140 
   141             SetFriendlyName();
   142 
   143             //Get our HID descriptor pre-parsed data
   144             PreParsedData = Win32.RawInput.GetPreParsedData(hRawInputDevice);
   145 
   146             if (PreParsedData == IntPtr.Zero)
   147             {
   148                 //We are done then.
   149                 //Some devices don't have pre-parsed data.
   150                 return;
   151             }
   152 
   153             //Get capabilities
   154             HidStatus status = Win32.Function.HidP_GetCaps(PreParsedData, ref iCapabilities);
   155             if (status != HidStatus.HIDP_STATUS_SUCCESS)
   156             {
   157                 throw new Exception("HidDevice: HidP_GetCaps failed: " + status.ToString());
   158             }
   159 
   160             //Get input button caps if needed
   161             if (Capabilities.NumberInputButtonCaps > 0)
   162             {
   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)
   167                 {
   168                     throw new Exception("HidDevice: HidP_GetButtonCaps failed: " + status.ToString());
   169                 }
   170             }
   171 
   172             //Get input value caps if needed
   173             if (Capabilities.NumberInputValueCaps > 0)
   174             {
   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)
   179                 {
   180                     throw new Exception("HidDevice: HidP_GetValueCaps failed: " + status.ToString());
   181                 }
   182             }
   183 
   184 
   185             
   186 
   187         }
   188 
   189         /// <summary>
   190         /// 
   191         /// </summary>
   192         private void SetFriendlyName()
   193         {
   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.
   196             string suffix = "";
   197             Type usageCollectionType = null;
   198             if (Info.dwType == RawInputDeviceType.RIM_TYPEHID)
   199             {
   200                 //Process usage page
   201                 if (Enum.IsDefined(typeof(Hid.UsagePage), Info.hid.usUsagePage))
   202                 {
   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);
   207                 }
   208                 else
   209                 {
   210                     //We don't know this usage page, add its value
   211                     suffix += " ( 0x" + Info.hid.usUsagePage.ToString("X4") + ", ";
   212                 }
   213 
   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))
   217                 {
   218                     //Show Hexa
   219                     suffix += "0x" + Info.hid.usUsage.ToString("X4") + " )";
   220                 }
   221                 else
   222                 {
   223                     //We know this usage page, add its name
   224                     suffix += Enum.GetName(usageCollectionType, Info.hid.usUsage) + " )";
   225                 }
   226             }
   227             else if (Info.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD)
   228             {
   229                 suffix = " - Keyboard";
   230             }
   231             else if (Info.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
   232             {
   233                 suffix = " - Mouse";
   234             }
   235 
   236             if (Product != null && Product.Length > 1)
   237             {
   238                 //Add the devices with a proper name at the top
   239                 FriendlyName = Product + suffix;
   240             }
   241             else
   242             {
   243                 //Add other once at the bottom
   244                 FriendlyName = "0x" + ProductId.ToString("X4") + suffix;
   245             }
   246 
   247         }
   248 
   249         /// <summary>
   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
   253         /// </summary>
   254         public void Dispose()
   255         {
   256             Marshal.FreeHGlobal(PreParsedData);
   257             PreParsedData = IntPtr.Zero;
   258         }
   259 
   260         /// <summary>
   261         /// Print information about this device to our debug output.
   262         /// </summary>
   263         public void DebugWrite()
   264         {
   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("==============================================================================================================");
   273         }
   274 
   275     }
   276 
   277 }