USB HDI通信类:
1 internal class HIDDevice 2 { 3 #region constants 4 private const int DIGCF_DEFAULT = 0x1; 5 private const int DIGCF_PRESENT = 0x2; 6 private const int DIGCF_ALLCLASSES = 0x4; 7 private const int DIGCF_PROFILE = 0x8; 8 private const int DIGCF_DEVICEINTERFACE = 0x10; 9 10 private const short FILE_ATTRIBUTE_NORMAL = 0x80; 11 private const short INVALID_HANDLE_VALUE = -1; 12 private const uint GENERIC_READ = 0x80000000; 13 private const uint GENERIC_WRITE = 0x40000000; 14 private const uint FILE_SHARE_READ = 0x00000001; 15 private const uint FILE_SHARE_WRITE = 0x00000002; 16 private const uint CREATE_NEW = 1; 17 private const uint CREATE_ALWAYS = 2; 18 private const uint OPEN_EXISTING = 3; 19 20 #endregion 21 22 #region win32_API_declarations 23 [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] 24 static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, 25 IntPtr Enumerator, 26 IntPtr hwndParent, 27 uint Flags); 28 29 [DllImport("hid.dll", SetLastError = true)] 30 private static extern void HidD_GetHidGuid(ref Guid hidGuid); 31 32 [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] 33 private static extern Boolean SetupDiEnumDeviceInterfaces( 34 IntPtr hDevInfo, 35 //ref SP_DEVINFO_DATA devInfo, 36 IntPtr devInfo, 37 ref Guid interfaceClassGuid, 38 UInt32 memberIndex, 39 ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData 40 ); 41 42 [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] 43 private static extern Boolean SetupDiGetDeviceInterfaceDetail( 44 IntPtr hDevInfo, 45 ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, 46 ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, 47 UInt32 deviceInterfaceDetailDataSize, 48 out UInt32 requiredSize, 49 ref SP_DEVINFO_DATA deviceInfoData 50 ); 51 52 [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] 53 private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); 54 55 [DllImport("kernel32.dll", SetLastError = true)] 56 private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, 57 uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, 58 uint dwFlagsAndAttributes, IntPtr hTemplateFile); 59 60 [DllImport("kernel32.dll", SetLastError = true)] 61 private static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer, 62 uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped); 63 64 [DllImport("kernel32.dll", SetLastError = true)] 65 private static extern bool WriteFile(SafeFileHandle hFile, byte[] lpBuffer, 66 uint nNumberOfBytesToWrite, ref uint lpNumberOfBytesWritten, IntPtr lpOverlapped); 67 68 [DllImport("hid.dll", SetLastError = true)] 69 private static extern bool HidD_GetPreparsedData( 70 SafeFileHandle hObject, 71 ref IntPtr PreparsedData); 72 73 [DllImport("hid.dll", SetLastError = true)] 74 private static extern Boolean HidD_FreePreparsedData(ref IntPtr PreparsedData); 75 76 [DllImport("hid.dll", SetLastError = true)] 77 private static extern int HidP_GetCaps( 78 IntPtr pPHIDP_PREPARSED_DATA, // IN PHIDP_PREPARSED_DATA PreparsedData, 79 ref HIDP_CAPS myPHIDP_CAPS); // OUT PHIDP_CAPS Capabilities 80 81 [DllImport("hid.dll", SetLastError = true)] 82 private static extern Boolean HidD_GetAttributes(SafeFileHandle hObject, ref HIDD_ATTRIBUTES Attributes); 83 84 [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 85 private static extern bool HidD_GetFeature( 86 IntPtr hDevice, 87 IntPtr hReportBuffer, 88 uint ReportBufferLength); 89 90 [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 91 private static extern bool HidD_SetFeature( 92 IntPtr hDevice, 93 IntPtr ReportBuffer, 94 uint ReportBufferLength); 95 96 [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 97 private static extern bool HidD_GetProductString( 98 SafeFileHandle hDevice, 99 IntPtr Buffer, 100 uint BufferLength); 101 102 [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 103 private static extern bool HidD_GetSerialNumberString( 104 SafeFileHandle hDevice, 105 IntPtr Buffer, 106 uint BufferLength); 107 108 [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] 109 private static extern Boolean HidD_GetManufacturerString( 110 SafeFileHandle hDevice, 111 IntPtr Buffer, 112 uint BufferLength); 113 114 #endregion 115 116 #region structs 117 118 public struct interfaceDetails 119 { 120 public string manufacturer; 121 public string product; 122 public int serialNumber; 123 public ushort VID; 124 public ushort PID; 125 public string devicePath; 126 public int IN_reportByteLength; 127 public int OUT_reportByteLength; 128 public ushort versionNumber; 129 } 130 131 // HIDP_CAPS 132 [StructLayout(LayoutKind.Sequential)] 133 private struct HIDP_CAPS 134 { 135 public System.UInt16 Usage; // USHORT 136 public System.UInt16 UsagePage; // USHORT 137 public System.UInt16 InputReportByteLength; 138 public System.UInt16 OutputReportByteLength; 139 public System.UInt16 FeatureReportByteLength; 140 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] 141 public System.UInt16[] Reserved; // USHORT Reserved[17]; 142 public System.UInt16 NumberLinkCollectionNodes; 143 public System.UInt16 NumberInputButtonCaps; 144 public System.UInt16 NumberInputValueCaps; 145 public System.UInt16 NumberInputDataIndices; 146 public System.UInt16 NumberOutputButtonCaps; 147 public System.UInt16 NumberOutputValueCaps; 148 public System.UInt16 NumberOutputDataIndices; 149 public System.UInt16 NumberFeatureButtonCaps; 150 public System.UInt16 NumberFeatureValueCaps; 151 public System.UInt16 NumberFeatureDataIndices; 152 } 153 154 [StructLayout(LayoutKind.Sequential)] 155 private struct SP_DEVINFO_DATA 156 { 157 public uint cbSize; 158 public Guid ClassGuid; 159 public uint DevInst; 160 public IntPtr Reserved; 161 } 162 [StructLayout(LayoutKind.Sequential)] 163 private struct SP_DEVICE_INTERFACE_DATA 164 { 165 public uint cbSize; 166 public Guid InterfaceClassGuid; 167 public uint Flags; 168 public IntPtr Reserved; 169 } 170 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 171 private struct SP_DEVICE_INTERFACE_DETAIL_DATA 172 { 173 public int cbSize; 174 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 175 public string DevicePath; 176 } 177 178 [StructLayout(LayoutKind.Sequential)] 179 private struct HIDD_ATTRIBUTES 180 { 181 public Int32 Size; 182 public Int16 VendorID; 183 public Int16 ProductID; 184 public Int16 VersionNumber; 185 } 186 187 [StructLayout(LayoutKind.Sequential)] 188 private struct COMMTIMEOUTS 189 { 190 public UInt32 ReadIntervalTimeout; 191 public UInt32 ReadTotalTimeoutMultiplier; 192 public UInt32 ReadTotalTimeoutConstant; 193 public UInt32 WriteTotalTimeoutMultiplier; 194 public UInt32 WriteTotalTimeoutConstant; 195 } 196 197 #endregion 198 199 #region globals 200 public bool deviceConnected { get; set; } 201 private SafeFileHandle handle_read; 202 private SafeFileHandle handle_write; 203 private FileStream FS_read; 204 private FileStream FS_write; 205 private HIDP_CAPS capabilities; 206 public interfaceDetails productInfo; 207 public event dataReceivedEvent dataReceived; //The calling class can subscribe to this event 208 public delegate void dataReceivedEvent(byte[] message); 209 public byte[] readData; 210 private bool useAsyncReads; 211 212 #endregion 213 214 #region static_methods 215 216 public static interfaceDetails[] getConnectedDevices() 217 { 218 interfaceDetails[] devices = new interfaceDetails[0]; 219 220 //Create structs to hold interface information 221 SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA(); 222 SP_DEVICE_INTERFACE_DATA devIface = new SP_DEVICE_INTERFACE_DATA(); 223 devInfo.cbSize = (uint)Marshal.SizeOf(devInfo); 224 devIface.cbSize = (uint)(Marshal.SizeOf(devIface)); 225 226 Guid G = new Guid(); 227 HidD_GetHidGuid(ref G); //Get the guid of the HID device class 228 229 IntPtr i = SetupDiGetClassDevs(ref G, IntPtr.Zero, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); 230 231 //Loop through all available entries in the device list, until false 232 SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA(); 233 if (IntPtr.Size == 8) // for 64 bit operating systems 234 didd.cbSize = 8; 235 else 236 didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // for 32 bit systems 237 238 int j = -1; 239 bool b = true; 240 int error; 241 SafeFileHandle tempHandle; 242 243 while (b) 244 { 245 j++; 246 247 b = SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref G, (uint)j, ref devIface); 248 error = Marshal.GetLastWin32Error(); 249 if (b == false) 250 break; 251 252 uint requiredSize = 0; 253 bool b1 = SetupDiGetDeviceInterfaceDetail(i, ref devIface, ref didd, 256, out requiredSize, ref devInfo); 254 string devicePath = didd.DevicePath; 255 256 //create file handles using CT_CreateFile 257 tempHandle = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 258 IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 259 260 //get capabilites - use getPreParsedData, and getCaps 261 //store the reportlengths 262 IntPtr ptrToPreParsedData = new IntPtr(); 263 bool ppdSucsess = HidD_GetPreparsedData(tempHandle, ref ptrToPreParsedData); 264 if (ppdSucsess == false) 265 continue; 266 267 HIDP_CAPS capabilities = new HIDP_CAPS(); 268 int hidCapsSucsess = HidP_GetCaps(ptrToPreParsedData, ref capabilities); 269 270 HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES(); 271 bool hidAttribSucsess = HidD_GetAttributes(tempHandle, ref attributes); 272 273 string productName = ""; 274 string SN = ""; 275 string manfString = ""; 276 IntPtr buffer = Marshal.AllocHGlobal(126);//max alloc for string; 277 if (HidD_GetProductString(tempHandle, buffer, 126)) productName = Marshal.PtrToStringAuto(buffer); 278 if (HidD_GetSerialNumberString(tempHandle, buffer, 126)) SN = Marshal.PtrToStringAuto(buffer); 279 if (HidD_GetManufacturerString(tempHandle, buffer, 126)) manfString = Marshal.PtrToStringAuto(buffer); 280 Marshal.FreeHGlobal(buffer); 281 282 //Call freePreParsedData to release some stuff 283 HidD_FreePreparsedData(ref ptrToPreParsedData); 284 285 //If connection was sucsessful, record the values in a global struct 286 interfaceDetails productInfo = new interfaceDetails(); 287 productInfo.devicePath = devicePath; 288 productInfo.manufacturer = manfString; 289 productInfo.product = productName; 290 productInfo.PID = (ushort)attributes.ProductID; 291 productInfo.VID = (ushort)attributes.VendorID; 292 productInfo.versionNumber = (ushort)attributes.VersionNumber; 293 productInfo.IN_reportByteLength = (int)capabilities.InputReportByteLength; 294 productInfo.OUT_reportByteLength = (int)capabilities.OutputReportByteLength; 295 296 if (stringIsInteger(SN)) 297 productInfo.serialNumber = Convert.ToInt32(SN); //Check that serial number is actually a number 298 299 int newSize = devices.Length + 1; 300 Array.Resize(ref devices, newSize); 301 devices[newSize - 1] = productInfo; 302 } 303 SetupDiDestroyDeviceInfoList(i); 304 305 return devices; 306 } 307 308 #endregion 309 310 #region constructors 311 /// <summary> 312 /// Creates an object to handle read/write functionality for a USB HID device 313 /// Uses one filestream for each of read/write to allow for a write to occur during a blocking 314 /// asnychronous read 315 /// </summary> 316 /// <param name="VID">The vendor ID of the USB device to connect to</param> 317 /// <param name="PID">The product ID of the USB device to connect to</param> 318 /// <param name="serialNumber">The serial number of the USB device to connect to</param> 319 /// <param name="useAsyncReads">True - Read the device and generate events on data being available</param> 320 public HIDDevice(ushort VID, ushort PID, int serialNumber, bool useAsyncReads) 321 { 322 interfaceDetails[] devices = getConnectedDevices(); 323 324 //loop through all connected devices to find one with the correct details 325 for (int i = 0; i < devices.Length; i++) 326 { 327 if ((devices[i].VID == VID) && (devices[i].PID == PID) && (devices[i].serialNumber == (int)serialNumber)) 328 initDevice(devices[i].devicePath, useAsyncReads); 329 } 330 331 if (!deviceConnected) 332 { 333 string hexVID = numToHexString(VID); 334 string hexPID = numToHexString(PID); 335 throw new Exception("Device with VID: 0x" + hexVID + " PID: 0x" + hexPID + " SerialNumber: " + serialNumber.ToString() + " could not be found"); 336 } 337 } 338 339 /// <summary> 340 /// Creates an object to handle read/write functionality for a USB HID device 341 /// Uses one filestream for each of read/write to allow for a write to occur during a blocking 342 /// asnychronous read 343 /// </summary> 344 /// <param name="devicePath">The USB device path - from getConnectedDevices</param> 345 /// <param name="useAsyncReads">True - Read the device and generate events on data being available</param> 346 public HIDDevice(string devicePath, bool useAsyncReads) 347 { 348 initDevice(devicePath, useAsyncReads); 349 350 if (!deviceConnected) 351 { 352 throw new Exception("Device could not be found"); 353 } 354 } 355 356 357 public HIDDevice(interfaceDetails productInfox) 358 { 359 360 361 362 363 364 365 deviceConnected = false; 366 367 //create file handles using CT_CreateFile 368 handle_read = CreateFile(productInfox.devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 369 IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 370 371 handle_write = CreateFile(productInfox.devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 372 IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 373 374 //get capabilites - use getPreParsedData, and getCaps 375 //store the reportlengths 376 IntPtr ptrToPreParsedData = new IntPtr(); 377 bool ppdSucsess = HidD_GetPreparsedData(handle_read, ref ptrToPreParsedData); 378 379 capabilities = new HIDP_CAPS(); 380 int hidCapsSucsess = HidP_GetCaps(ptrToPreParsedData, ref capabilities); 381 382 HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES(); 383 bool hidAttribSucsess = HidD_GetAttributes(handle_read, ref attributes); 384 385 386 387 //Call freePreParsedData to release some stuff 388 HidD_FreePreparsedData(ref ptrToPreParsedData); 389 //SetupDiDestroyDeviceInfoList(i); 390 391 if (handle_read.IsInvalid) 392 return; 393 394 deviceConnected = true; 395 396 //If connection was sucsessful, record the values in a global struct 397 productInfo = productInfox; 398 399 400 //use a filestream object to bring this stuff into .NET 401 FS_write = new FileStream(handle_write, FileAccess.ReadWrite, capabilities.InputReportByteLength, false); 402 if (capabilities.OutputReportByteLength > 0) 403 { 404 FS_read = new FileStream(handle_read, FileAccess.ReadWrite, capabilities.OutputReportByteLength, false); 405 } 406 407 408 409 this.useAsyncReads = true; 410 411 readAsync(); 412 413 414 } 415 416 417 #endregion 418 419 #region functions 420 private void initDevice(string devicePath, bool useAsyncReads) 421 { 422 deviceConnected = false; 423 424 //create file handles using CT_CreateFile 425 handle_read = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 426 IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 427 428 handle_write = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 429 IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); 430 431 //get capabilites - use getPreParsedData, and getCaps 432 //store the reportlengths 433 IntPtr ptrToPreParsedData = new IntPtr(); 434 bool ppdSucsess = HidD_GetPreparsedData(handle_read, ref ptrToPreParsedData); 435 436 capabilities = new HIDP_CAPS(); 437 int hidCapsSucsess = HidP_GetCaps(ptrToPreParsedData, ref capabilities); 438 439 HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES(); 440 bool hidAttribSucsess = HidD_GetAttributes(handle_read, ref attributes); 441 442 string productName = ""; 443 string SN = ""; 444 string manfString = ""; 445 IntPtr buffer = Marshal.AllocHGlobal(126);//max alloc for string; 446 if (HidD_GetProductString(handle_read, buffer, 126)) productName = Marshal.PtrToStringAuto(buffer); 447 if (HidD_GetSerialNumberString(handle_read, buffer, 126)) SN = Marshal.PtrToStringAuto(buffer); 448 if (HidD_GetManufacturerString(handle_read, buffer, 126)) manfString = Marshal.PtrToStringAuto(buffer); 449 Marshal.FreeHGlobal(buffer); 450 451 //Call freePreParsedData to release some stuff 452 HidD_FreePreparsedData(ref ptrToPreParsedData); 453 //SetupDiDestroyDeviceInfoList(i); 454 455 if (handle_read.IsInvalid) 456 return; 457 458 deviceConnected = true; 459 460 //If connection was sucsessful, record the values in a global struct 461 productInfo = new interfaceDetails(); 462 productInfo.devicePath = devicePath; 463 productInfo.manufacturer = manfString; 464 productInfo.product = productName; 465 productInfo.serialNumber = Convert.ToInt32(SN); 466 productInfo.PID = (ushort)attributes.ProductID; 467 productInfo.VID = (ushort)attributes.VendorID; 468 productInfo.versionNumber = (ushort)attributes.VersionNumber; 469 productInfo.IN_reportByteLength = (int)capabilities.InputReportByteLength; 470 productInfo.OUT_reportByteLength = (int)capabilities.OutputReportByteLength; 471 472 //use a filestream object to bring this stuff into .NET 473 FS_read = new FileStream(handle_read, FileAccess.ReadWrite, capabilities.OutputReportByteLength, false); 474 FS_write = new FileStream(handle_write, FileAccess.ReadWrite, capabilities.InputReportByteLength, false); 475 476 this.useAsyncReads = useAsyncReads; 477 if (useAsyncReads) 478 readAsync(); 479 } 480 481 public void close() 482 { 483 if (FS_read != null) 484 FS_read.Close(); 485 if (FS_write != null) 486 FS_write.Close(); 487 488 if ((handle_read != null) && (!(handle_read.IsInvalid))) 489 handle_read.Close(); 490 if ((handle_write != null) && (!(handle_write.IsInvalid))) 491 handle_write.Close(); 492 493 this.deviceConnected = false; 494 } 495 496 public void write(byte[] data) 497 { 498 //if (data.Length > capabilities.OutputReportByteLength) 499 // throw new Exception("Output report must not exceed " + (capabilities.OutputReportByteLength - 1).ToString() + " bytes"); 500 501 //uint numBytesWritten = 0; 502 byte[] packet = new byte[capabilities.OutputReportByteLength]; 503 Array.Copy(data, 0, packet, 1, data.Length); //start at 1, as the first byte must be zero for HID report 504 packet[0] = 0; 505 506 if (FS_write.CanWrite) 507 FS_write.Write(packet, 0, packet.Length); 508 else 509 { 510 this.close(); 511 //throw new Exception("Device is unable to read"); 512 } 513 } 514 515 //This read function will be used with asychronous operation, called by the constructor if async reads are used 516 private void readAsync() 517 { 518 readData = new byte[capabilities.InputReportByteLength]; 519 if (FS_read.CanRead) 520 FS_read.BeginRead(readData, 0, readData.Length, new AsyncCallback(GetInputReportData), readData); 521 else 522 { 523 this.close(); 524 //throw new Exception("Device is unable to read"); 525 } 526 } 527 528 private void GetInputReportData(IAsyncResult ar) 529 { 530 FS_read.EndRead(ar); //must call an endread before starting another one 531 //TODO handle exception with PCB is reaet 532 533 //Reset the read thread to read the next report 534 if (FS_read.CanRead) 535 FS_read.BeginRead(readData, 0, readData.Length, new AsyncCallback(GetInputReportData), readData); 536 else 537 { 538 this.close(); 539 //throw new Exception("Device is unable to read"); 540 } 541 dataReceived(readData); //triggers the event to be heard by the calling class 542 } 543 544 /// <summary> 545 /// This read function is for normal synchronous reads 546 /// </summary> 547 /// <returns></returns> 548 public byte[] read() 549 { 550 if (useAsyncReads == true) 551 throw new Exception("A synchonous read cannot be executed when operating in async mode"); 552 553 //Call readFile 554 byte[] readBuf = new byte[capabilities.InputReportByteLength]; 555 FS_read.Read(readBuf, 0, readBuf.Length); 556 return readBuf; 557 } 558 #endregion 559 560 #region utilities 561 562 public static bool stringIsInteger(string val) 563 { 564 Double result; 565 return Double.TryParse(val, System.Globalization.NumberStyles.Integer, 566 System.Globalization.CultureInfo.CurrentCulture, out result); 567 } 568 569 public static string numToHexString(ushort num) 570 { 571 return String.Format("{0:X}", num); 572 } 573 574 #endregion 575 }
使用方法:
HIDDevice device; HIDDevice.interfaceDetails[] devices; devices = HIDDevice.getConnectedDevices(); device = new HIDDevice(devices[i]); device.dataReceived += new HIDDevice.dataReceivedEvent(device_dataReceived); device.write(byte[]);
void device_dataReceived(byte[] message) { }