├── .gitignore
├── HID_Demo.sln
├── LICENSE
├── README.md
├── Properties
└── AssemblyInfo.cs
├── HID_Demo.csproj
├── Program.cs
└── HIDInterface.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | [Bb]in
2 | [Oo]bj
3 | *.suo
4 | *.user
--------------------------------------------------------------------------------
/HID_Demo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HID_Demo", "HID_Demo.csproj", "{DEB4DCCC-72AC-4057-BF73-BF35BC3BD117}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {DEB4DCCC-72AC-4057-BF73-BF35BC3BD117}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {DEB4DCCC-72AC-4057-BF73-BF35BC3BD117}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {DEB4DCCC-72AC-4057-BF73-BF35BC3BD117}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {DEB4DCCC-72AC-4057-BF73-BF35BC3BD117}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 jcoenraadts
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hid-sharp
2 |
3 | Hid-Sharp is designed to take the pain out of communicating with HID (human interface devices) in Windows.
4 |
5 | For an existing project, all you require is the HIDInterface.cs class. This class is a wrapper for the Win32 API calls required for USB operation. It implements simple read/write functions and generates events on asynchronous transmissions.
6 |
7 | The code will build under .NET 2.0, and uses only the APIs available as far back as WinXP. It hsa been tested under XP, Vista and Win7 and Win8
8 |
9 | Also included is a well documented demo of the functionality of the class. This is a console app which implements synchronous and asynchonous operation of the class.
10 |
11 | ## Example
12 | ```
13 | //Get the details of all connected USB HID devices
14 | HIDDevice.interfaceDetails[] devices = HIDDevice.getConnectedDevices();
15 |
16 | //Select a device from the available devices
17 | string devicePath = devices[selectedDeviceIndex].devicePath;
18 |
19 | //create a handle to the device by calling the constructor
20 | HIDDevice device = new HIDDevice(devicePath, false);
21 |
22 | //Write a byte array to the device
23 | byte[] writeData = { 0x00, 0x01, 0x02, 0x03, 0x04 };
24 | device.write(writeData); //Its that easy!!
25 |
26 | //Read a byte array from the device
27 | byte[] readData = device.read(); //again, that easy!
28 |
29 | //close the device to release all handles etc
30 | device.close();
31 | ```
32 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("HID_Demo")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("HID_Demo")]
13 | [assembly: AssemblyCopyright("Copyright © 2011")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("66df7691-18d8-4a9a-9fef-a22748ae1d6f")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/HID_Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {DEB4DCCC-72AC-4057-BF73-BF35BC3BD117}
9 | Exe
10 | Properties
11 | HID_Demo
12 | HID_Demo
13 | v3.5
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 |
41 | 3.5
42 |
43 |
44 | 3.5
45 |
46 |
47 | 3.5
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using HIDInterface;
6 |
7 | namespace HID_Demo
8 | {
9 | class Program
10 | {
11 | /*------------------------------------------------------------------------------------------
12 | * HID Interface class demo code
13 | *
14 | * This demo code returns the details of all connected HID devices, then selects one of them
15 | * and connects to it. A synchronous read / write operation is performed and the device is
16 | * closed.
17 | * Two methods are included to show the synchronous and asynchronous operations of this software.
18 | *
19 | * The intention of this code is to provide a demonstration which can be run in a debug
20 | * environment, and the sample methods can be cut and pasted into your program
21 | * ----------------------------------------------------------------------------------------*/
22 |
23 | static void Main(string[] args)
24 | {
25 | HID_demo demo = new HID_demo();
26 |
27 | //call one or other of these methods to demonstrate each type of operation - sync and async
28 | demo.startAsyncOperation();
29 | //demo.useSynchronousOperation();
30 | }
31 | }
32 |
33 | public class HID_demo
34 | {
35 |
36 | // Apologies for the repeated code, however i feel it provides a better demonstration
37 | // of the functionality of this code.
38 | public void useSynchronousOperation()
39 | {
40 | //Get the details of all connected USB HID devices
41 | HIDDevice.interfaceDetails[] devices = HIDDevice.getConnectedDevices();
42 |
43 | //Arbitrarily select one of the devices which we found in the previous step
44 | //record the details of this device to be used in the class constructor
45 | int selectedDeviceIndex = 0;
46 | ushort VID = devices[selectedDeviceIndex].VID;
47 | ushort PID = devices[selectedDeviceIndex].PID;
48 | int SN = devices[selectedDeviceIndex].serialNumber;
49 | string devicePath = devices[selectedDeviceIndex].devicePath;
50 |
51 | //create a handle to the device by calling the constructor of the HID class
52 | //This can be done using either the VID/PID/Serialnumber, or the device path (string)
53 | //all of these details are available from the HIDDevice.interfaceDetails[] struct array created above
54 | //The "false" boolean in the constructor tells the class we only want synchronous operation
55 | HIDDevice device = new HIDDevice(devicePath, false);
56 | //OR, the normal usage when you know the VID and PID of the device
57 | //HIDDevice device = new HIDDevice(VID, PID, (ushort)SN, false);
58 |
59 | //Write some data to the device (the write method throws an exception if the data is longer than the report length
60 | //specified by the device, this length can be found in the HIDDevice.interfaceDetails struct)
61 | byte[] writeData = { 0x00, 0x01, 0x02, 0x03, 0x04 };
62 | device.write(writeData); //Its that easy!!
63 |
64 | //Read some data synchronously from the device. This method blocks the calling thread until the data
65 | //is returned. This takes 1-20ms for most HID devices
66 | byte[] readData = device.read(); //again, that easy!
67 |
68 | //close the device to release all handles etc
69 | device.close();
70 | }
71 |
72 | public void startAsyncOperation()
73 | {
74 | //Get the details of all connected USB HID devices
75 | HIDDevice.interfaceDetails[] devices = HIDDevice.getConnectedDevices();
76 |
77 | //Arbitrarily select one of the devices which we found in the previous step
78 | //record the details of this device to be used in the class constructor
79 | int selectedDeviceIndex = 0;
80 | ushort VID = devices[selectedDeviceIndex].VID;
81 | ushort PID = devices[selectedDeviceIndex].PID;
82 | int SN = devices[selectedDeviceIndex].serialNumber;
83 | string devicePath = devices[selectedDeviceIndex].devicePath;
84 |
85 | //create a handle to the device by calling the constructor of the HID class
86 | //This can be done using either the VID/PID/Serialnumber, or the device path (string)
87 | //all of these details are available from the HIDDevice.interfaceDetails[] struct array created above
88 | //The "true" boolean in the constructor tells the class we want asynchronous operation this time
89 | HIDDevice device = new HIDDevice(devicePath, true);
90 | //OR, the normal usage when you know the VID and PID of the device
91 | //HIDDevice device = new HIDDevice(VID, PID, (ushort)SN, true);
92 |
93 | //next create the event handler for the incoming reports
94 | device.dataReceived += new HIDDevice.dataReceivedEvent(device_dataReceived);
95 |
96 | //Write some data to the device (the write method throws an exception if the data is longer than the report length
97 | //specified by the device, this length can be found in the HIDDevice.interfaceDetails struct)
98 | //The write method is identical to the synchronous mode of operation
99 | byte[] writeData = { 0x00, 0x01, 0x02, 0x03, 0x04 };
100 | device.write(writeData); //Its that easy!!
101 |
102 | //Send your program off to do other stuff here, wait for UI events etc
103 | //When a report occurs, the device_dataReceived(byte[] message) method will be called
104 | System.Threading.Thread.Sleep(100);
105 |
106 | //close the device to release all handles etc
107 | device.close();
108 | }
109 |
110 | //Whenever a report comes in, this method will be called and the data will be available! Like magic...
111 | void device_dataReceived(byte[] message)
112 | {
113 | //Do something with the data here...
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/HIDInterface.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Runtime.InteropServices;
5 | using System.Threading;
6 | using Microsoft.Win32.SafeHandles;
7 | using System.IO;
8 |
9 | namespace HIDInterface
10 | {
11 | internal class HIDDevice
12 | {
13 | #region constants
14 | private const int DIGCF_DEFAULT = 0x1;
15 | private const int DIGCF_PRESENT = 0x2;
16 | private const int DIGCF_ALLCLASSES = 0x4;
17 | private const int DIGCF_PROFILE = 0x8;
18 | private const int DIGCF_DEVICEINTERFACE = 0x10;
19 |
20 | private const short FILE_ATTRIBUTE_NORMAL = 0x80;
21 | private const short INVALID_HANDLE_VALUE = -1;
22 | private const uint GENERIC_READ = 0x80000000;
23 | private const uint GENERIC_WRITE = 0x40000000;
24 | private const uint FILE_SHARE_READ = 0x00000001;
25 | private const uint FILE_SHARE_WRITE = 0x00000002;
26 | private const uint CREATE_NEW = 1;
27 | private const uint CREATE_ALWAYS = 2;
28 | private const uint OPEN_EXISTING = 3;
29 |
30 | #endregion
31 |
32 | #region win32_API_declarations
33 | [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
34 | static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
35 | IntPtr Enumerator,
36 | IntPtr hwndParent,
37 | uint Flags);
38 |
39 | [DllImport("hid.dll", SetLastError = true)]
40 | private static extern void HidD_GetHidGuid(ref Guid hidGuid);
41 |
42 | [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
43 | private static extern Boolean SetupDiEnumDeviceInterfaces(
44 | IntPtr hDevInfo,
45 | //ref SP_DEVINFO_DATA devInfo,
46 | IntPtr devInfo,
47 | ref Guid interfaceClassGuid,
48 | UInt32 memberIndex,
49 | ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
50 | );
51 |
52 | [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
53 | private static extern Boolean SetupDiGetDeviceInterfaceDetail(
54 | IntPtr hDevInfo,
55 | ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
56 | ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
57 | UInt32 deviceInterfaceDetailDataSize,
58 | out UInt32 requiredSize,
59 | ref SP_DEVINFO_DATA deviceInfoData
60 | );
61 |
62 | [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
63 | private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
64 |
65 | [DllImport("kernel32.dll", SetLastError = true)]
66 | private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
67 | uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
68 | uint dwFlagsAndAttributes, IntPtr hTemplateFile);
69 |
70 | [DllImport("kernel32.dll", SetLastError = true)]
71 | private static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer,
72 | uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped);
73 |
74 | [DllImport("kernel32.dll", SetLastError = true)]
75 | private static extern bool WriteFile(SafeFileHandle hFile, byte[] lpBuffer,
76 | uint nNumberOfBytesToWrite, ref uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
77 |
78 | [DllImport("hid.dll", SetLastError = true)]
79 | private static extern bool HidD_GetPreparsedData(
80 | SafeFileHandle hObject,
81 | ref IntPtr PreparsedData);
82 |
83 | [DllImport("hid.dll", SetLastError = true)]
84 | private static extern Boolean HidD_FreePreparsedData(ref IntPtr PreparsedData);
85 |
86 | [DllImport("hid.dll", SetLastError = true)]
87 | private static extern int HidP_GetCaps(
88 | IntPtr pPHIDP_PREPARSED_DATA, // IN PHIDP_PREPARSED_DATA PreparsedData,
89 | ref HIDP_CAPS myPHIDP_CAPS); // OUT PHIDP_CAPS Capabilities
90 |
91 | [DllImport("hid.dll", SetLastError = true)]
92 | private static extern Boolean HidD_GetAttributes(SafeFileHandle hObject, ref HIDD_ATTRIBUTES Attributes);
93 |
94 | [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
95 | private static extern bool HidD_GetFeature(
96 | IntPtr hDevice,
97 | IntPtr hReportBuffer,
98 | uint ReportBufferLength);
99 |
100 | [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
101 | private static extern bool HidD_SetFeature(
102 | IntPtr hDevice,
103 | IntPtr ReportBuffer,
104 | uint ReportBufferLength);
105 |
106 | [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
107 | private static extern bool HidD_GetProductString(
108 | SafeFileHandle hDevice,
109 | IntPtr Buffer,
110 | uint BufferLength);
111 |
112 | [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
113 | private static extern bool HidD_GetSerialNumberString(
114 | SafeFileHandle hDevice,
115 | IntPtr Buffer,
116 | uint BufferLength);
117 |
118 | [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
119 | private static extern Boolean HidD_GetManufacturerString(
120 | SafeFileHandle hDevice,
121 | IntPtr Buffer,
122 | uint BufferLength);
123 |
124 | #endregion
125 |
126 | #region structs
127 |
128 | public struct interfaceDetails
129 | {
130 | public string manufacturer;
131 | public string product;
132 | public int serialNumber;
133 | public ushort VID;
134 | public ushort PID;
135 | public string devicePath;
136 | public int IN_reportByteLength;
137 | public int OUT_reportByteLength;
138 | public ushort versionNumber;
139 | }
140 |
141 | // HIDP_CAPS
142 | [StructLayout(LayoutKind.Sequential)]
143 | private struct HIDP_CAPS
144 | {
145 | public System.UInt16 Usage; // USHORT
146 | public System.UInt16 UsagePage; // USHORT
147 | public System.UInt16 InputReportByteLength;
148 | public System.UInt16 OutputReportByteLength;
149 | public System.UInt16 FeatureReportByteLength;
150 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
151 | public System.UInt16[] Reserved; // USHORT Reserved[17];
152 | public System.UInt16 NumberLinkCollectionNodes;
153 | public System.UInt16 NumberInputButtonCaps;
154 | public System.UInt16 NumberInputValueCaps;
155 | public System.UInt16 NumberInputDataIndices;
156 | public System.UInt16 NumberOutputButtonCaps;
157 | public System.UInt16 NumberOutputValueCaps;
158 | public System.UInt16 NumberOutputDataIndices;
159 | public System.UInt16 NumberFeatureButtonCaps;
160 | public System.UInt16 NumberFeatureValueCaps;
161 | public System.UInt16 NumberFeatureDataIndices;
162 | }
163 |
164 | [StructLayout(LayoutKind.Sequential)]
165 | private struct SP_DEVINFO_DATA
166 | {
167 | public uint cbSize;
168 | public Guid ClassGuid;
169 | public uint DevInst;
170 | public IntPtr Reserved;
171 | }
172 | [StructLayout(LayoutKind.Sequential)]
173 | private struct SP_DEVICE_INTERFACE_DATA
174 | {
175 | public uint cbSize;
176 | public Guid InterfaceClassGuid;
177 | public uint Flags;
178 | public IntPtr Reserved;
179 | }
180 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
181 | private struct SP_DEVICE_INTERFACE_DETAIL_DATA
182 | {
183 | public int cbSize;
184 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
185 | public string DevicePath;
186 | }
187 |
188 | [StructLayout(LayoutKind.Sequential)]
189 | private struct HIDD_ATTRIBUTES
190 | {
191 | public Int32 Size;
192 | public Int16 VendorID;
193 | public Int16 ProductID;
194 | public Int16 VersionNumber;
195 | }
196 |
197 | [StructLayout(LayoutKind.Sequential)]
198 | private struct COMMTIMEOUTS
199 | {
200 | public UInt32 ReadIntervalTimeout;
201 | public UInt32 ReadTotalTimeoutMultiplier;
202 | public UInt32 ReadTotalTimeoutConstant;
203 | public UInt32 WriteTotalTimeoutMultiplier;
204 | public UInt32 WriteTotalTimeoutConstant;
205 | }
206 |
207 | #endregion
208 |
209 | #region globals
210 | public bool deviceConnected { get; set; }
211 | private SafeFileHandle handle_read;
212 | private SafeFileHandle handle_write;
213 | private FileStream FS_read;
214 | private FileStream FS_write;
215 | private HIDP_CAPS capabilities;
216 | public interfaceDetails productInfo;
217 | public event dataReceivedEvent dataReceived; //The calling class can subscribe to this event
218 | public delegate void dataReceivedEvent(byte[] message);
219 | public byte[] readData;
220 | private bool useAsyncReads;
221 |
222 | #endregion
223 |
224 | #region static_methods
225 |
226 | public static interfaceDetails[] getConnectedDevices()
227 | {
228 | interfaceDetails[] devices = new interfaceDetails[0];
229 |
230 | //Create structs to hold interface information
231 | SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA();
232 | SP_DEVICE_INTERFACE_DATA devIface = new SP_DEVICE_INTERFACE_DATA();
233 | devInfo.cbSize = (uint)Marshal.SizeOf(devInfo);
234 | devIface.cbSize = (uint)(Marshal.SizeOf(devIface));
235 |
236 | Guid G = new Guid();
237 | HidD_GetHidGuid(ref G); //Get the guid of the HID device class
238 |
239 | IntPtr i = SetupDiGetClassDevs(ref G, IntPtr.Zero, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
240 |
241 | //Loop through all available entries in the device list, until false
242 | SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA();
243 | if (IntPtr.Size == 8) // for 64 bit operating systems
244 | didd.cbSize = 8;
245 | else
246 | didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // for 32 bit systems
247 |
248 | int j = -1;
249 | bool b = true;
250 | int error;
251 | SafeFileHandle tempHandle;
252 |
253 | while (b)
254 | {
255 | j++;
256 |
257 | b = SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref G, (uint)j, ref devIface);
258 | error = Marshal.GetLastWin32Error();
259 | if (b == false)
260 | break;
261 |
262 | uint requiredSize = 0;
263 | bool b1 = SetupDiGetDeviceInterfaceDetail(i, ref devIface, ref didd, 256, out requiredSize, ref devInfo);
264 | string devicePath = didd.DevicePath;
265 |
266 | //create file handles using CT_CreateFile
267 | tempHandle = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
268 | IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
269 |
270 | //get capabilites - use getPreParsedData, and getCaps
271 | //store the reportlengths
272 | IntPtr ptrToPreParsedData = new IntPtr();
273 | bool ppdSucsess = HidD_GetPreparsedData(tempHandle, ref ptrToPreParsedData);
274 | if (ppdSucsess == false)
275 | continue;
276 |
277 | HIDP_CAPS capabilities = new HIDP_CAPS();
278 | int hidCapsSucsess = HidP_GetCaps(ptrToPreParsedData, ref capabilities);
279 |
280 | HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES();
281 | bool hidAttribSucsess = HidD_GetAttributes(tempHandle, ref attributes);
282 |
283 | string productName = "";
284 | string SN = "";
285 | string manfString = "";
286 | IntPtr buffer = Marshal.AllocHGlobal(126);//max alloc for string;
287 | if (HidD_GetProductString(tempHandle, buffer, 126)) productName = Marshal.PtrToStringAuto(buffer);
288 | if (HidD_GetSerialNumberString(tempHandle, buffer, 126)) SN = Marshal.PtrToStringAuto(buffer);
289 | if (HidD_GetManufacturerString(tempHandle, buffer, 126)) manfString = Marshal.PtrToStringAuto(buffer);
290 | Marshal.FreeHGlobal(buffer);
291 |
292 | //Call freePreParsedData to release some stuff
293 | HidD_FreePreparsedData(ref ptrToPreParsedData);
294 |
295 | //If connection was sucsessful, record the values in a global struct
296 | interfaceDetails productInfo = new interfaceDetails();
297 | productInfo.devicePath = devicePath;
298 | productInfo.manufacturer = manfString;
299 | productInfo.product = productName;
300 | productInfo.PID = (ushort)attributes.ProductID;
301 | productInfo.VID = (ushort)attributes.VendorID;
302 | productInfo.versionNumber = (ushort)attributes.VersionNumber;
303 | productInfo.IN_reportByteLength = (int)capabilities.InputReportByteLength;
304 | productInfo.OUT_reportByteLength = (int)capabilities.OutputReportByteLength;
305 |
306 | if (stringIsInteger(SN))
307 | productInfo.serialNumber = Convert.ToInt32(SN); //Check that serial number is actually a number
308 |
309 | int newSize = devices.Length + 1;
310 | Array.Resize(ref devices, newSize);
311 | devices[newSize - 1] = productInfo;
312 | }
313 | SetupDiDestroyDeviceInfoList(i);
314 |
315 | return devices;
316 | }
317 |
318 | #endregion
319 |
320 | #region constructors
321 | ///
322 | /// Creates an object to handle read/write functionality for a USB HID device
323 | /// Uses one filestream for each of read/write to allow for a write to occur during a blocking
324 | /// asnychronous read
325 | ///
326 | /// The vendor ID of the USB device to connect to
327 | /// The product ID of the USB device to connect to
328 | /// The serial number of the USB device to connect to
329 | /// True - Read the device and generate events on data being available
330 | public HIDDevice(ushort VID, ushort PID, ushort serialNumber, bool useAsyncReads)
331 | {
332 | interfaceDetails[] devices = getConnectedDevices();
333 |
334 | //loop through all connected devices to find one with the correct details
335 | for (int i = 0; i < devices.Length; i++)
336 | {
337 | if ((devices[i].VID == VID) && (devices[i].PID == PID) && (devices[i].serialNumber == (int)serialNumber))
338 | initDevice(devices[i].devicePath, useAsyncReads);
339 | }
340 |
341 | if (!deviceConnected)
342 | {
343 | string hexVID = numToHexString(VID);
344 | string hexPID = numToHexString(PID);
345 | throw new Exception("Device with VID: 0x" + hexVID + " PID: 0x" + hexPID + " SerialNumber: " + serialNumber.ToString() + " could not be found");
346 | }
347 | }
348 |
349 | ///
350 | /// Creates an object to handle read/write functionality for a USB HID device
351 | /// Uses one filestream for each of read/write to allow for a write to occur during a blocking
352 | /// asnychronous read
353 | ///
354 | /// The USB device path - from getConnectedDevices
355 | /// True - Read the device and generate events on data being available
356 | public HIDDevice(string devicePath, bool useAsyncReads)
357 | {
358 | initDevice(devicePath, useAsyncReads);
359 |
360 | if (!deviceConnected)
361 | {
362 | throw new Exception("Device could not be found");
363 | }
364 | }
365 | #endregion
366 |
367 | #region functions
368 | private void initDevice(string devicePath, bool useAsyncReads)
369 | {
370 | deviceConnected = false;
371 |
372 | //create file handles using CT_CreateFile
373 | handle_read = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
374 | IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
375 |
376 | handle_write = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
377 | IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
378 |
379 | //get capabilites - use getPreParsedData, and getCaps
380 | //store the reportlengths
381 | IntPtr ptrToPreParsedData = new IntPtr();
382 | bool ppdSucsess = HidD_GetPreparsedData(handle_read, ref ptrToPreParsedData);
383 |
384 | capabilities = new HIDP_CAPS();
385 | int hidCapsSucsess = HidP_GetCaps(ptrToPreParsedData, ref capabilities);
386 |
387 | HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES();
388 | bool hidAttribSucsess = HidD_GetAttributes(handle_read, ref attributes);
389 |
390 | string productName = "";
391 | string SN = "";
392 | string manfString = "";
393 | IntPtr buffer = Marshal.AllocHGlobal(126);//max alloc for string;
394 | if (HidD_GetProductString(handle_read, buffer, 126)) productName = Marshal.PtrToStringAuto(buffer);
395 | if (HidD_GetSerialNumberString(handle_read, buffer, 126)) SN = Marshal.PtrToStringAuto(buffer);
396 | if (HidD_GetManufacturerString(handle_read, buffer, 126)) manfString = Marshal.PtrToStringAuto(buffer);
397 | Marshal.FreeHGlobal(buffer);
398 |
399 | //Call freePreParsedData to release some stuff
400 | HidD_FreePreparsedData(ref ptrToPreParsedData);
401 | //SetupDiDestroyDeviceInfoList(i);
402 |
403 | if (handle_read.IsInvalid)
404 | return;
405 |
406 | deviceConnected = true;
407 |
408 | //If connection was sucsessful, record the values in a global struct
409 | productInfo = new interfaceDetails();
410 | productInfo.devicePath = devicePath;
411 | productInfo.manufacturer = manfString;
412 | productInfo.product = productName;
413 | productInfo.serialNumber = Convert.ToInt32(SN);
414 | productInfo.PID = (ushort)attributes.ProductID;
415 | productInfo.VID = (ushort)attributes.VendorID;
416 | productInfo.versionNumber = (ushort)attributes.VersionNumber;
417 | productInfo.IN_reportByteLength = (int)capabilities.InputReportByteLength;
418 | productInfo.OUT_reportByteLength = (int)capabilities.OutputReportByteLength;
419 |
420 | //use a filestream object to bring this stuff into .NET
421 | FS_read = new FileStream(handle_read, FileAccess.ReadWrite, capabilities.OutputReportByteLength, false);
422 | FS_write = new FileStream(handle_write, FileAccess.ReadWrite, capabilities.InputReportByteLength, false);
423 |
424 | this.useAsyncReads = useAsyncReads;
425 | if (useAsyncReads)
426 | readAsync();
427 | }
428 |
429 | public void close()
430 | {
431 | if (FS_read != null)
432 | FS_read.Close();
433 | if (FS_write != null)
434 | FS_write.Close();
435 |
436 | if ((handle_read != null) && (!(handle_read.IsInvalid)))
437 | handle_read.Close();
438 | if ((handle_write != null) && (!(handle_write.IsInvalid)))
439 | handle_write.Close();
440 |
441 | this.deviceConnected = false;
442 | }
443 |
444 | public void write(byte[] data)
445 | {
446 | if (data.Length > capabilities.OutputReportByteLength)
447 | throw new Exception("Output report must not exceed " + (capabilities.OutputReportByteLength - 1).ToString() + " bytes");
448 |
449 | //uint numBytesWritten = 0;
450 | byte[] packet = new byte[capabilities.OutputReportByteLength];
451 | Array.Copy(data, 0, packet, 1, data.Length); //start at 1, as the first byte must be zero for HID report
452 | packet[0] = 0;
453 |
454 | if (FS_write.CanWrite)
455 | FS_write.Write(packet, 0, packet.Length);
456 | else
457 | throw new Exception("Filestream unable to write");
458 | }
459 |
460 | //This read function will be used with asychronous operation, called by the constructor if async reads are used
461 | private void readAsync()
462 | {
463 | readData = new byte[capabilities.InputReportByteLength];
464 | if (FS_read.CanRead)
465 | FS_read.BeginRead(readData, 0, readData.Length, new AsyncCallback(GetInputReportData), readData);
466 | else
467 | throw new Exception("Device is unable to read");
468 | }
469 |
470 | private void GetInputReportData(IAsyncResult ar)
471 | {
472 | FS_read.EndRead(ar); //must call an endread before starting another one
473 | //TODO handle exception with PCB is reaet
474 |
475 | //Reset the read thread to read the next report
476 | if (FS_read.CanRead)
477 | FS_read.BeginRead(readData, 0, readData.Length, new AsyncCallback(GetInputReportData), readData);
478 | else
479 | throw new Exception("Device is unable to read");
480 |
481 | dataReceived(readData); //triggers the event to be heard by the calling class
482 | }
483 |
484 | ///
485 | /// This read function is for normal synchronous reads
486 | ///
487 | ///
488 | public byte[] read()
489 | {
490 | if (useAsyncReads == true)
491 | throw new Exception("A synchonous read cannot be executed when operating in async mode");
492 |
493 | //Call readFile
494 | byte[] readBuf = new byte[capabilities.InputReportByteLength];
495 | FS_read.Read(readBuf, 0, readBuf.Length);
496 | return readBuf;
497 | }
498 | #endregion
499 |
500 | #region utilities
501 |
502 | public static bool stringIsInteger(string val)
503 | {
504 | Double result;
505 | return Double.TryParse(val, System.Globalization.NumberStyles.Integer,
506 | System.Globalization.CultureInfo.CurrentCulture, out result);
507 | }
508 |
509 | public static string numToHexString(ushort num)
510 | {
511 | return String.Format("{0:X}", num);
512 | }
513 |
514 | #endregion
515 | }
516 | }
--------------------------------------------------------------------------------