范例下载地址:https://download.csdn.net/download/u010875635/10689911
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
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Windows;
namespace HID_SIMPLE.HID
{
public class report : EventArgs
{
public readonly byte reportID;
public readonly byte[] reportBuff;
public report(byte id, byte[] arrayBuff)
{
reportID = id;
reportBuff = arrayBuff;
}
}
public class Hid : object
{
private IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private const int MAX_USB_DEVICES = 64;
private bool deviceOpened = false;
private FileStream hidDevice = null;
private IntPtr hHubDevice;
int outputReportLength;//输出报告长度,包刮一个字节的报告ID
public int OutputReportLength { get { return outputReportLength; } }
int inputReportLength;//输入报告长度,包刮一个字节的报告ID
public int InputReportLength { get { return inputReportLength; } }
/// <summary>
/// 打开指定信息的设备
/// </summary>
/// <param name="vID">设备的vID</param>
/// <param name="pID">设备的pID</param>
/// <param name="serial">设备的serial</param>
/// <returns></returns>
public HID_RETURN OpenDevice(UInt16 vID, UInt16 pID, string serial)
{
if (deviceOpened == false)
{
//获取连接的HID列表
List<string> deviceList = new List<string>();
GetHidDeviceList(ref deviceList);
if (deviceList.Count == 0)
return HID_RETURN.NO_DEVICE_CONECTED;
for (int i = 0; i < deviceList.Count; i++)
{
IntPtr device = CreateFile(deviceList[i],
DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
0,
0,
CREATIONDISPOSITION.OPEN_EXISTING,
FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED,
0);
if (device != INVALID_HANDLE_VALUE)
{
HIDD_ATTRIBUTES attributes;
IntPtr serialBuff = Marshal.AllocHGlobal(512);
HidD_GetAttributes(device, out attributes);
HidD_GetSerialNumberString(device, serialBuff, 512);
string deviceStr = Marshal.PtrToStringAuto(serialBuff);
Marshal.FreeHGlobal(serialBuff);
if (attributes.VendorID == vID && attributes.ProductID == pID && deviceStr.Contains(serial))
{
IntPtr preparseData;
HIDP_CAPS caps;
HidD_GetPreparsedData(device, out preparseData);
HidP_GetCaps(preparseData, out caps);
HidD_FreePreparsedData(preparseData);
outputReportLength = caps.OutputReportByteLength;
inputReportLength = caps.InputReportByteLength;
hidDevice = new FileStream(new SafeFileHandle(device, false), FileAccess.ReadWrite, inputReportLength, true);
deviceOpened = true;
BeginAsyncRead();
hHubDevice = device;
return HID_RETURN.SUCCESS;
}
}
}
return HID_RETURN.DEVICE_NOT_FIND;
}
else
return HID_RETURN.DEVICE_OPENED;
}
/// <summary>
/// 关闭打开的设备
/// </summary>
public void CloseDevice()
{
if (deviceOpened == true)
{
deviceOpened = false;
hidDevice.Close();
}
}
/// <summary>
/// 开始一次异步读
/// </summary>
private void BeginAsyncRead()
{
byte[] inputBuff = new byte[InputReportLength];
hidDevice.BeginRead(inputBuff, 0, InputReportLength, new AsyncCallback(ReadCompleted), inputBuff);
}
/// <summary>
/// 异步读取结束,发出有数据到达事件
/// </summary>
/// <param name="iResult">这里是输入报告的数组</param>
private void ReadCompleted(IAsyncResult iResult)
{
byte[] readBuff = (byte[])(iResult.AsyncState);
try
{
hidDevice.EndRead(iResult);//读取结束,如果读取错误就会产生一个异常
byte[] reportData = new byte[readBuff.Length - 1];
for (int i = 1; i < readBuff.Length; i++)
reportData[i - 1] = readBuff[i];
report e = new report(readBuff[0], reportData);
OnDataReceived(e); //发出数据到达消息
if (!deviceOpened) return;
BeginAsyncRead();//启动下一次读操作
}
catch //读写错误,设备已经被移除
{
//MyConsole.WriteLine("设备无法连接,请重新插入设备");
EventArgs ex = new EventArgs();
OnDeviceRemoved(ex);//发出设备移除消息
CloseDevice();
}
}
public delegate void DelegateDataReceived(object sender, report e);
//public event EventHandler<ConnectEventArg> StatusConnected;
public DelegateDataReceived DataReceived;
/// <summary>
/// 事件:数据到达,处理此事件以接收输入数据
/// </summary>
protected virtual void OnDataReceived(report e)
{
if (DataReceived != null) DataReceived(this, e);
}
/// <summary>
/// 事件:设备断开
/// </summary>
public delegate void DelegateStatusConnected(object sender, EventArgs e);
public DelegateStatusConnected DeviceRemoved;
protected virtual void OnDeviceRemoved(EventArgs e)
{
if (DeviceRemoved != null) DeviceRemoved(this,