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.
sl@25
     1
using System;
sl@25
     2
using System.Windows.Forms;
sl@25
     3
using System.Runtime.InteropServices;
sl@25
     4
using System.Diagnostics;
sl@25
     5
using System.Text;
sl@25
     6
using Microsoft.Win32.SafeHandles;
StephaneLenclud@53
     7
using Win32;
sl@25
     8
sl@25
     9
namespace Hid
sl@25
    10
{
sl@25
    11
    /// <summary>
sl@25
    12
    /// Represent a HID device.
StephaneLenclud@60
    13
    /// Rename to RawInputDevice?
sl@25
    14
    /// </summary>
StephaneLenclud@52
    15
    public class HidDevice: IDisposable
sl@25
    16
    {
StephaneLenclud@63
    17
        /// <summary>
StephaneLenclud@63
    18
        /// Unique name of that HID device.
StephaneLenclud@63
    19
        /// Notably used as input to CreateFile.
StephaneLenclud@63
    20
        /// </summary>
sl@25
    21
        public string Name { get; private set; }
StephaneLenclud@63
    22
StephaneLenclud@63
    23
        /// <summary>
StephaneLenclud@63
    24
        /// Friendly name that people should be able to read.
StephaneLenclud@63
    25
        /// </summary>
StephaneLenclud@63
    26
        public string FriendlyName { get; private set; }
StephaneLenclud@63
    27
StephaneLenclud@63
    28
        //
sl@25
    29
        public string Manufacturer { get; private set; }
sl@25
    30
        public string Product { get; private set; }
sl@25
    31
        public ushort VendorId { get; private set; }
sl@25
    32
        public ushort ProductId { get; private set; }
sl@25
    33
        public ushort Version { get; private set; }
StephaneLenclud@56
    34
        //Pre-parsed HID descriptor
StephaneLenclud@52
    35
        public IntPtr PreParsedData {get; private set;}
StephaneLenclud@56
    36
        //Info
StephaneLenclud@53
    37
        public RID_DEVICE_INFO Info { get {return iInfo;} }
StephaneLenclud@53
    38
        private RID_DEVICE_INFO iInfo;
StephaneLenclud@56
    39
        //Capabilities
StephaneLenclud@56
    40
        public HIDP_CAPS Capabilities { get { return iCapabilities; } }
StephaneLenclud@56
    41
        private HIDP_CAPS iCapabilities;
StephaneLenclud@56
    42
        //Input Button Capabilities
StephaneLenclud@56
    43
        public HIDP_BUTTON_CAPS[] InputButtonCapabilities { get { return iInputButtonCapabilities; } }
StephaneLenclud@56
    44
        private HIDP_BUTTON_CAPS[] iInputButtonCapabilities;
StephaneLenclud@58
    45
        //Input Value Capabilities
StephaneLenclud@58
    46
        public HIDP_VALUE_CAPS[] InputValueCapabilities { get { return iInputValueCapabilities; } }
StephaneLenclud@58
    47
        private HIDP_VALUE_CAPS[] iInputValueCapabilities;
StephaneLenclud@58
    48
StephaneLenclud@58
    49
        
StephaneLenclud@56
    50
        
sl@25
    51
sl@25
    52
        /// <summary>
sl@26
    53
        /// Class constructor will fetch this object properties from HID sub system.
sl@25
    54
        /// </summary>
sl@26
    55
        /// <param name="hRawInputDevice">Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice</param>
sl@25
    56
        public HidDevice(IntPtr hRawInputDevice)
sl@25
    57
        {
StephaneLenclud@55
    58
            //Try construct and rollback if needed
StephaneLenclud@55
    59
            try
StephaneLenclud@55
    60
            {
StephaneLenclud@55
    61
                Construct(hRawInputDevice);
StephaneLenclud@55
    62
            }
StephaneLenclud@55
    63
            catch (System.Exception ex)
StephaneLenclud@55
    64
            {
StephaneLenclud@55
    65
                //Just rollback and propagate
StephaneLenclud@55
    66
                Dispose();
StephaneLenclud@55
    67
                throw ex;
StephaneLenclud@55
    68
            }
StephaneLenclud@55
    69
        }
StephaneLenclud@55
    70
StephaneLenclud@55
    71
StephaneLenclud@55
    72
        /// <summary>
StephaneLenclud@55
    73
        /// Make sure dispose is called even if the user forgot about it.
StephaneLenclud@55
    74
        /// </summary>
StephaneLenclud@55
    75
        ~HidDevice()
StephaneLenclud@55
    76
        {
StephaneLenclud@55
    77
            Dispose();
StephaneLenclud@55
    78
        }
StephaneLenclud@55
    79
StephaneLenclud@55
    80
        /// <summary>
StephaneLenclud@55
    81
        /// Private constructor.
StephaneLenclud@55
    82
        /// </summary>
StephaneLenclud@55
    83
        /// <param name="hRawInputDevice"></param>
StephaneLenclud@55
    84
        private void Construct(IntPtr hRawInputDevice)
StephaneLenclud@55
    85
        {
StephaneLenclud@52
    86
            PreParsedData = IntPtr.Zero;
StephaneLenclud@58
    87
            iInputButtonCapabilities = null;
StephaneLenclud@58
    88
            iInputValueCapabilities = null;
StephaneLenclud@58
    89
sl@25
    90
            //Fetch various information defining the given HID device
StephaneLenclud@60
    91
            Name = Win32.RawInput.GetDeviceName(hRawInputDevice);
StephaneLenclud@52
    92
StephaneLenclud@53
    93
            //Fetch device info
StephaneLenclud@53
    94
            iInfo = new RID_DEVICE_INFO();
StephaneLenclud@60
    95
            if (!Win32.RawInput.GetDeviceInfo(hRawInputDevice, ref iInfo))
StephaneLenclud@53
    96
            {
StephaneLenclud@55
    97
                throw new Exception("HidDevice: GetDeviceInfo failed: " + Marshal.GetLastWin32Error().ToString());
StephaneLenclud@53
    98
            }
StephaneLenclud@55
    99
sl@25
   100
            //Open our device from the device name/path
StephaneLenclud@55
   101
            SafeFileHandle handle = Win32.Function.CreateFile(Name,
sl@25
   102
                Win32.FileAccess.NONE,
StephaneLenclud@55
   103
                Win32.FileShare.FILE_SHARE_READ | Win32.FileShare.FILE_SHARE_WRITE,
sl@25
   104
                IntPtr.Zero,
sl@25
   105
                Win32.CreationDisposition.OPEN_EXISTING,
sl@25
   106
                Win32.FileFlagsAttributes.FILE_FLAG_OVERLAPPED,
sl@25
   107
                IntPtr.Zero
sl@25
   108
                );
sl@25
   109
StephaneLenclud@55
   110
            //Check if CreateFile worked
sl@25
   111
            if (handle.IsInvalid)
sl@25
   112
            {
StephaneLenclud@55
   113
                throw new Exception("HidDevice: CreateFile failed: " + Marshal.GetLastWin32Error().ToString());
sl@25
   114
            }
StephaneLenclud@55
   115
StephaneLenclud@55
   116
            //Get manufacturer string
StephaneLenclud@55
   117
            StringBuilder manufacturerString = new StringBuilder(256);
StephaneLenclud@55
   118
            if (Win32.Function.HidD_GetManufacturerString(handle, manufacturerString, manufacturerString.Capacity))
sl@25
   119
            {
StephaneLenclud@55
   120
                Manufacturer = manufacturerString.ToString();
StephaneLenclud@55
   121
            }
sl@25
   122
StephaneLenclud@55
   123
            //Get product string
StephaneLenclud@55
   124
            StringBuilder productString = new StringBuilder(256);
StephaneLenclud@55
   125
            if (Win32.Function.HidD_GetProductString(handle, productString, productString.Capacity))
StephaneLenclud@55
   126
            {
StephaneLenclud@55
   127
                Product = productString.ToString();
StephaneLenclud@55
   128
            }
sl@25
   129
StephaneLenclud@55
   130
            //Get attributes
StephaneLenclud@55
   131
            Win32.HIDD_ATTRIBUTES attributes = new Win32.HIDD_ATTRIBUTES();
StephaneLenclud@55
   132
            if (Win32.Function.HidD_GetAttributes(handle, ref attributes))
StephaneLenclud@55
   133
            {
StephaneLenclud@55
   134
                VendorId = attributes.VendorID;
StephaneLenclud@55
   135
                ProductId = attributes.ProductID;
StephaneLenclud@55
   136
                Version = attributes.VersionNumber;
StephaneLenclud@55
   137
            }
sl@25
   138
StephaneLenclud@55
   139
            handle.Close();
StephaneLenclud@56
   140
StephaneLenclud@63
   141
            SetFriendlyName();
StephaneLenclud@63
   142
StephaneLenclud@61
   143
            //Get our HID descriptor pre-parsed data
StephaneLenclud@61
   144
            PreParsedData = Win32.RawInput.GetPreParsedData(hRawInputDevice);
StephaneLenclud@61
   145
StephaneLenclud@61
   146
            if (PreParsedData == IntPtr.Zero)
StephaneLenclud@61
   147
            {
StephaneLenclud@61
   148
                //We are done then.
StephaneLenclud@61
   149
                //Some devices don't have pre-parsed data.
StephaneLenclud@61
   150
                return;
StephaneLenclud@61
   151
            }
StephaneLenclud@61
   152
StephaneLenclud@56
   153
            //Get capabilities
StephaneLenclud@56
   154
            HidStatus status = Win32.Function.HidP_GetCaps(PreParsedData, ref iCapabilities);
StephaneLenclud@56
   155
            if (status != HidStatus.HIDP_STATUS_SUCCESS)
StephaneLenclud@56
   156
            {
StephaneLenclud@56
   157
                throw new Exception("HidDevice: HidP_GetCaps failed: " + status.ToString());
StephaneLenclud@56
   158
            }
StephaneLenclud@56
   159
StephaneLenclud@58
   160
            //Get input button caps if needed
StephaneLenclud@58
   161
            if (Capabilities.NumberInputButtonCaps > 0)
StephaneLenclud@56
   162
            {
StephaneLenclud@58
   163
                iInputButtonCapabilities = new HIDP_BUTTON_CAPS[Capabilities.NumberInputButtonCaps];
StephaneLenclud@58
   164
                ushort buttonCapabilitiesLength = Capabilities.NumberInputButtonCaps;
StephaneLenclud@58
   165
                status = Win32.Function.HidP_GetButtonCaps(HIDP_REPORT_TYPE.HidP_Input, iInputButtonCapabilities, ref buttonCapabilitiesLength, PreParsedData);
StephaneLenclud@58
   166
                if (status != HidStatus.HIDP_STATUS_SUCCESS || buttonCapabilitiesLength != Capabilities.NumberInputButtonCaps)
StephaneLenclud@58
   167
                {
StephaneLenclud@58
   168
                    throw new Exception("HidDevice: HidP_GetButtonCaps failed: " + status.ToString());
StephaneLenclud@58
   169
                }
StephaneLenclud@56
   170
            }
StephaneLenclud@58
   171
StephaneLenclud@58
   172
            //Get input value caps if needed
StephaneLenclud@58
   173
            if (Capabilities.NumberInputValueCaps > 0)
StephaneLenclud@58
   174
            {
StephaneLenclud@58
   175
                iInputValueCapabilities = new HIDP_VALUE_CAPS[Capabilities.NumberInputValueCaps];
StephaneLenclud@58
   176
                ushort valueCapabilitiesLength = Capabilities.NumberInputValueCaps;
StephaneLenclud@58
   177
                status = Win32.Function.HidP_GetValueCaps(HIDP_REPORT_TYPE.HidP_Input, iInputValueCapabilities, ref valueCapabilitiesLength, PreParsedData);
StephaneLenclud@58
   178
                if (status != HidStatus.HIDP_STATUS_SUCCESS || valueCapabilitiesLength != Capabilities.NumberInputValueCaps)
StephaneLenclud@58
   179
                {
StephaneLenclud@58
   180
                    throw new Exception("HidDevice: HidP_GetValueCaps failed: " + status.ToString());
StephaneLenclud@58
   181
                }
StephaneLenclud@58
   182
            }
StephaneLenclud@58
   183
StephaneLenclud@56
   184
StephaneLenclud@63
   185
            
StephaneLenclud@63
   186
StephaneLenclud@63
   187
        }
StephaneLenclud@63
   188
StephaneLenclud@63
   189
        /// <summary>
StephaneLenclud@63
   190
        /// 
StephaneLenclud@63
   191
        /// </summary>
StephaneLenclud@63
   192
        private void SetFriendlyName()
StephaneLenclud@63
   193
        {
StephaneLenclud@63
   194
            //Work out proper suffix for our device root node.
StephaneLenclud@63
   195
            //That allows users to see in a glance what kind of device this is.
StephaneLenclud@63
   196
            string suffix = "";
StephaneLenclud@63
   197
            Type usageCollectionType = null;
StephaneLenclud@63
   198
            if (Info.dwType == RawInputDeviceType.RIM_TYPEHID)
StephaneLenclud@63
   199
            {
StephaneLenclud@63
   200
                //Process usage page
StephaneLenclud@63
   201
                if (Enum.IsDefined(typeof(Hid.UsagePage), Info.hid.usUsagePage))
StephaneLenclud@63
   202
                {
StephaneLenclud@63
   203
                    //We know this usage page, add its name
StephaneLenclud@63
   204
                    Hid.UsagePage usagePage = (Hid.UsagePage)Info.hid.usUsagePage;
StephaneLenclud@63
   205
                    suffix += " ( " + usagePage.ToString() + ", ";
StephaneLenclud@63
   206
                    usageCollectionType = Hid.Utils.UsageCollectionType(usagePage);
StephaneLenclud@63
   207
                }
StephaneLenclud@63
   208
                else
StephaneLenclud@63
   209
                {
StephaneLenclud@63
   210
                    //We don't know this usage page, add its value
StephaneLenclud@63
   211
                    suffix += " ( 0x" + Info.hid.usUsagePage.ToString("X4") + ", ";
StephaneLenclud@63
   212
                }
StephaneLenclud@63
   213
StephaneLenclud@63
   214
                //Process usage collection
StephaneLenclud@63
   215
                //We don't know this usage page, add its value
StephaneLenclud@63
   216
                if (usageCollectionType == null || !Enum.IsDefined(usageCollectionType, Info.hid.usUsage))
StephaneLenclud@63
   217
                {
StephaneLenclud@63
   218
                    //Show Hexa
StephaneLenclud@63
   219
                    suffix += "0x" + Info.hid.usUsage.ToString("X4") + " )";
StephaneLenclud@63
   220
                }
StephaneLenclud@63
   221
                else
StephaneLenclud@63
   222
                {
StephaneLenclud@63
   223
                    //We know this usage page, add its name
StephaneLenclud@63
   224
                    suffix += Enum.GetName(usageCollectionType, Info.hid.usUsage) + " )";
StephaneLenclud@63
   225
                }
StephaneLenclud@63
   226
            }
StephaneLenclud@63
   227
            else if (Info.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD)
StephaneLenclud@63
   228
            {
StephaneLenclud@63
   229
                suffix = " - Keyboard";
StephaneLenclud@63
   230
            }
StephaneLenclud@63
   231
            else if (Info.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
StephaneLenclud@63
   232
            {
StephaneLenclud@63
   233
                suffix = " - Mouse";
StephaneLenclud@63
   234
            }
StephaneLenclud@63
   235
StephaneLenclud@63
   236
            if (Product != null && Product.Length > 1)
StephaneLenclud@63
   237
            {
StephaneLenclud@63
   238
                //Add the devices with a proper name at the top
StephaneLenclud@63
   239
                FriendlyName = Product + suffix;
StephaneLenclud@63
   240
            }
StephaneLenclud@63
   241
            else
StephaneLenclud@63
   242
            {
StephaneLenclud@63
   243
                //Add other once at the bottom
StephaneLenclud@63
   244
                FriendlyName = "0x" + ProductId.ToString("X4") + suffix;
StephaneLenclud@63
   245
            }
StephaneLenclud@63
   246
StephaneLenclud@52
   247
        }
StephaneLenclud@52
   248
StephaneLenclud@52
   249
        /// <summary>
StephaneLenclud@52
   250
        /// Dispose is just for unmanaged clean-up.
StephaneLenclud@52
   251
        /// Make sure calling disposed multiple times does not crash.
StephaneLenclud@52
   252
        /// See: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface/538238#538238
StephaneLenclud@52
   253
        /// </summary>
StephaneLenclud@52
   254
        public void Dispose()
StephaneLenclud@52
   255
        {
StephaneLenclud@52
   256
            Marshal.FreeHGlobal(PreParsedData);
StephaneLenclud@52
   257
            PreParsedData = IntPtr.Zero;
StephaneLenclud@52
   258
        }
StephaneLenclud@52
   259
sl@25
   260
        /// <summary>
sl@25
   261
        /// Print information about this device to our debug output.
sl@25
   262
        /// </summary>
sl@25
   263
        public void DebugWrite()
sl@25
   264
        {
sl@25
   265
            Debug.WriteLine("================ HID =========================================================================================");
sl@25
   266
            Debug.WriteLine("==== Name: " + Name);
sl@25
   267
            Debug.WriteLine("==== Manufacturer: " + Manufacturer);
sl@25
   268
            Debug.WriteLine("==== Product: " + Product);
sl@25
   269
            Debug.WriteLine("==== VendorID: 0x" + VendorId.ToString("X4"));
sl@25
   270
            Debug.WriteLine("==== ProductID: 0x" + ProductId.ToString("X4"));
sl@25
   271
            Debug.WriteLine("==== Version: " + Version.ToString());
sl@25
   272
            Debug.WriteLine("==============================================================================================================");
sl@25
   273
        }
sl@25
   274
sl@25
   275
    }
sl@25
   276
sl@25
   277
}