本文转载于:https://blog.csdn.net/u010875635/article/details/73321066
Windows使用HID通信相对比较简单,HID都是通过PID、VID信息来查找连接的,相比于串口,几乎无变化,连接无需人工选择,十分方便,也不需要驱动。
下面上实例,PID为0x003f,VID为0x04D8,支持发送接收数据显示到UI,使用C#来编写,调用的是windows api(create file、read file、write file)。
本实例将HID接口分成3层,支持自动连接、断开状态通知,异步收发数据,单个数据包大小为64bytes(因为从设备的数据包设定为64bytes,保持一致)。
接口分为两层,第一层将create file、read file、write file封装,第二层再封装自动连接、异步收发。
Hid.cs -> HIDInterface.cs -> 应用
注意,这里所有数据都是64bytes,但是有用数据并非这么多,所以设定为第一个数据为后面数据实际长度,若需要修改定义,请在HIDInterface.cs的Send与HidDataReceived函数中修改处理方法即可。
Hid.cs


1 using System; 2 using System.Collections.Generic; 3 using System.Runtime.InteropServices; 4 using System.IO; 5 using Microsoft.Win32.SafeHandles; 6 using System.Windows; 7 8 namespace HID_SIMPLE.HID 9 { 10 public class report : EventArgs 11 { 12 public readonly byte reportID; 13 public readonly byte[] reportBuff; 14 public report(byte id, byte[] arrayBuff) 15 { 16 reportID = id; 17 reportBuff = arrayBuff; 18 } 19 } 20 public class Hid : object 21 { 22 private IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); 23 private const int MAX_USB_DEVICES = 64; 24 private bool deviceOpened = false; 25 private FileStream hidDevice = null; 26 private IntPtr hHubDevice; 27 28 int outputReportLength;//输出报告长度,包刮一个字节的报告ID 29 public int OutputReportLength { get { return outputReportLength; } } 30 int inputReportLength;//输入报告长度,包刮一个字节的报告ID 31 public int InputReportLength { get { return inputReportLength; } } 32 33 /// <summary> 34 /// 打开指定信息的设备 35 /// </summary> 36 /// <param name="vID">设备的vID</param> 37 /// <param name="pID">设备的pID</param> 38 /// <param name="serial">设备的serial</param> 39 /// <returns></returns> 40 public HID_RETURN OpenDevice(UInt16 vID, UInt16 pID, string serial) 41 { 42 if (deviceOpened == false) 43 { 44 //获取连接的HID列表 45 List<string> deviceList = new List<string>(); 46 GetHidDeviceList(ref deviceList); 47 if (deviceList.Count == 0) 48 return HID_RETURN.NO_DEVICE_CONECTED; 49 for (int i = 0; i < deviceList.Count; i++) 50 { 51 IntPtr device = CreateFile(deviceList[i], 52 DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE, 53 0, 54 0, 55 CREATIONDISPOSITION.OPEN_EXISTING, 56 FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED, 57 0); 58 if (device != INVALID_HANDLE_VALUE) 59 { 60 HIDD_ATTRIBUTES attributes; 61 IntPtr serialBuff = Marshal.AllocHGlobal(512); 62 HidD_GetAttributes(device, out attributes); 63 HidD_GetSerialNumberString(device, serialBuff, 512); 64 string deviceStr = Marshal.PtrToStringAuto(serialBuff); 65 Marshal.FreeHGlobal(serialBuff); 66 if (attributes.VendorID == vID && attributes.ProductID == pID && deviceStr.Contains(serial)) 67 { 68 IntPtr preparseData; 69 HIDP_CAPS caps; 70 HidD_GetPreparsedData(device, out preparseData); 71 HidP_GetCaps(preparseData, out caps); 72 HidD_FreePreparsedData(preparseData); 73 outputReportLength = caps.OutputReportByteLength; 74 inputReportLength = caps.InputReportByteLength; 75 76 hidDevice = new FileStream(new SafeFileHandle(device, false), FileAccess.ReadWrite, inputReportLength, true); 77 deviceOpened = true; 78 BeginAsyncRead(); 79 80 hHubDevice = device; 81 return HID_RETURN.SUCCESS; 82 } 83 } 84 } 85 return HID_RETURN.DEVICE_NOT_FIND; 86 } 87 else 88 return HID_RETURN.DEVICE_OPENED; 89 } 90 91 /// <summary> 92 /// 关闭打开的设备 93 /// </summary> 94 public void CloseDevice() 95 { 96 if (deviceOpened == true) 97 { 98 deviceOpened = false; 99 hidDevice.Close(); 100 } 101 } 102 103 /// <summary> 104 /// 开始一次异步读 105 /// </summary> 106 private void BeginAsyncRead() 107 { 108 byte[] inputBuff = new byte[InputReportLength]; 109 hidDevice.BeginRead(inputBuff, 0, InputReportLength, new AsyncCallback(ReadCompleted), inputBuff); 110 } 111 112 /// <summary> 113 /// 异步读取结束,发出有数据到达事件 114 /// </summary> 115 /// <param name="iResult">这里是输入报告的数组</param> 116 private void ReadCompleted(IAsyncResult iResult) 117 { 118 byte[] readBuff = (byte[])(iResult.AsyncState); 119 try 120 { 121 hidDevice.EndRead(iResult);//读取结束,如果读取错误就会产生一个异常 122 byte[] reportData = new byte[readBuff.Length - 1]; 123 for (int i = 1; i < readBuff.Length; i++) 124 reportData[i - 1] = readBuff[i]; 125 report e = new report(readBuff[0], reportData); 126 OnDataReceived(e); //发出数据到达消息 127 if (!deviceOpened) return; 128 BeginAsyncRead();//启动下一次读操作 129 } 130 catch //读写错误,设备已经被移除 131 { 132 //MyConsole.WriteLine("设备无法连接,请重新插入设备"); 133 EventArgs ex = new EventArgs(); 134 OnDeviceRemoved(ex);//发出设备移除消息 135 CloseDevice(); 136 137 } 138 } 139 140 public delegate void DelegateDataReceived(object sender, report e); 141 //public event EventHandler<ConnectEventArg> StatusConnected; 142 143 public DelegateDataReceived DataReceived; 144 145 /// <summary> 146 /// 事件:数据到达,处理此事件以接收输入数据 147 /// </summary> 148 149 protected virtual void OnDataReceived(report e) 150 { 151 if (DataReceived != null) DataReceived(this, e); 152 } 153 154 /// <summary> 155 /// 事件:设备断开 156 /// </summary> 157 158 public delegate void DelegateStatusConnected(object sender, EventArgs e); 159 public DelegateStatusConnected DeviceRemoved; 160 protected virtual void OnDeviceRemoved(EventArgs e) 161 { 162 if (DeviceRemoved != null) DeviceRemoved(this,e); 163 } 164 165 /// <summary> 166 /// 167 /// </summary> 168 /// <param name="buffer"></param> 169 /// <returns></returns> 170 public HID_RETURN Write(report r) 171 { 172 if (deviceOpened) 173 { 174 try 175 { 176 byte[] buffer = new byte