VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 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里面的某个值可能告诉我了。

趟了这些坑后,搞定了,能用线程了^-^,发文纪念。

相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号