当前位置:
首页 > Python基础教程 >
-
C#教程之C# 连蒙带骗不知所以然的搞定USB下位机读(2)
/// <summary> /// 获取所有Usb设备文件名 /// </summary> /// <returns></returns> public static List<string> GetUsbFileNames() { List<string> items = new List<string>(); //通过一个空的GUID来获取HID的全局GUID。 Guid hidGuid = Guid.Empty; HidD_GetHidGuid(ref hidGuid); //通过获取到的HID全局GUID来获取包含所有HID接口信息集合的句柄。 IntPtr hidInfoSet = SetupDiGetClassDevs(ref hidGuid, 0, IntPtr.Zero, DIGCF.DIGCF_PRESENT | DIGCF.DIGCF_DEVICEINTERFACE); //获取接口信息。 if (hidInfoSet != IntPtr.Zero) { SP_DEVICE_INTERFACE_DATA interfaceInfo = new SP_DEVICE_INTERFACE_DATA(); interfaceInfo.cbSize = Marshal.SizeOf(interfaceInfo); uint index = 0; //检测集合的每个接口 while (SetupDiEnumDeviceInterfaces(hidInfoSet, IntPtr.Zero, ref hidGuid, index, ref interfaceInfo)) { int bufferSize = 0; //获取接口详细信息;第一次读取错误,但可取得信息缓冲区的大小 SP_DEVINFO_DATA strtInterfaceData = new SP_DEVINFO_DATA(); var result = SetupDiGetDeviceInterfaceDetail(hidInfoSet, ref interfaceInfo, IntPtr.Zero, 0, ref bufferSize, null); //第二次调用传递返回值,调用即可成功 IntPtr detailDataBuffer = Marshal.AllocHGlobal(bufferSize); Marshal.StructureToPtr( new SP_DEVICE_INTERFACE_DETAIL_DATA { cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA)) }, detailDataBuffer, false); if (SetupDiGetDeviceInterfaceDetail(hidInfoSet, ref interfaceInfo, detailDataBuffer, bufferSize, ref bufferSize, null))// strtInterfaceData)) { string devicePath = Marshal.PtrToStringAuto(IntPtr.Add(detailDataBuffer, 4)); items.Add(devicePath); } Marshal.FreeHGlobal(detailDataBuffer); index++; } } //删除设备信息并释放内存 SetupDiDestroyDeviceInfoList(hidInfoSet); return items; }
一般会返回好几个文件名,那哪个是你要的呢?方法有二:
1.先获取一次文件名列表,然后插拔或者禁用启用一次Usb设备,变化的那个就是
2.轮流写然后读一次文件名,获取到正确结果的就是
我采用2,然后User.config里面把他记下来。
要读写,首先要打开
(二)打开Usb设备
/// <summary> /// 构造 /// </summary> /// <param name="usbFileName">Usb Device Path</param> public UsbApi(string usbFileName) { if (string.IsNullOrEmpty(usbFileName)) throw new Exception("文件名不能为空"); var fileHandle = CreateFile( usbFileName, GENERIC_READ | GENERIC_WRITE,// | GENERIC_WRITE,//读写,或者一起 FILE_SHARE_READ | FILE_SHARE_WRITE,//共享读写,或者一起 0, OPEN_EXISTING,//必须已经存在 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); if (fileHandle == IntPtr.Zero || (int)fileHandle == -1) throw new Exception("打开文件失败"); HidD_GetAttributes(fileHandle, out var attributes);// null);// out var aa); HidD_GetPreparsedData(fileHandle, out var preparseData); HidP_GetCaps(preparseData, out var caps); HidD_FreePreparsedData(preparseData); _InputBufferSize = caps.InputReportByteLength; _OutputBufferSize = caps.OutputReportByteLength; _UsbFileStream = new FileStream(new SafeFileHandle(fileHandle, true), FileAccess.ReadWrite, System.Math.Max(caps.OutputReportByteLength, caps.InputReportByteLength), true); }
打开Usb设备我是在构找函数里面完成的,我的类名叫UsbApi。
(三)写
/// <summary> /// 写数据 /// </summary> /// <param name="array"></param> public void Write(byte[] data) { if (_UsbFileStream == null) throw new Exception("Usb设备没有初始化"); if (data.Length > _OutputBufferSize) throw new Exception($"数据太长,超出缓冲区长度({_OutputBufferSize})"); byte[] outBuffer = new byte[_OutputBufferSize]; Array.Copy(data, 0, outBuffer, 1, data.Length); _UsbFileStream.Write(outBuffer, 0, _OutputBufferSize); }
(四)读
/// <summary> /// 同步读 /// </summary> /// <param name="array"></param> public byte[] Read() {
if (_UsbFileStream == null)
throw new Exception("Usb设备没有初始化");
byte[] inBuffer = new byte[_InputBufferSize]; _UsbFileStream.Read(inBuffer, 0, _InputBufferSize); return inBuffer; }
我的Usb设备是半双工的,并且数据只有64字节,所有用了同步读。
(五)关闭
public void Close() { if (_UsbFileStream != null) _UsbFileStream.Close(); }
六、最后
最后写了几行代码测试,巨坑:
1.CreateFile参数的坑
var fileHandle = CreateFile( usbFileName, GENERIC_READ | GENERIC_WRITE,// | GENERIC_WRITE,//读写,或者一起 FILE_SHARE_READ | FILE_SHARE_WRITE,//共享读写,或者一起 0, OPEN_EXISTING,//必须已经存在 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); 这些参数是针对我的Usb设备,各种调整后达到了能读写、能异步。
2.FileStream参数的坑
_UsbFileStream = new FileStream(new SafeFileHandle(fileHandle, true), FileAccess.ReadWrite, System.Math.Max(caps.OutputReportByteLength, caps.InputReportByteLength), true);
缓冲区大小最终采用 System.Math.Max(caps.OutputReportByteLength, caps.InputReportByteLength)
太小读写错误,大点似乎没关系
3.Write的巨坑
public void Write(byte[] data) 这个data长度必须与缓冲区大写一样,而且数据要从data[1]开始写,如你要写“AB”, var data=new byte[]{0,(byte)'A',(byte)'B'};
事后发现HIDP_CAPS里面的某个值可能告诉我了。 趟了这些坑后,搞定了,能用线程了^-^,发文纪念。
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式