所谓的潇洒

导航

USB设备读写

从C盘搜索kernel32.dll、setupapi.dll、hid.dll,并复制到项目debug目录

加入USBHelper.cs,代码如下

    public class USBHelper
    {
        bool result;
        string devicePathName;
        Guid guidHID = Guid.Empty;
        //指针
        IntPtr hDevInfo;
        IntPtr deviceHandle = IntPtr.Zero;       
        //CreateFile参数配置
        public const uint GENERIC_READ = 0x80000000;
        public const uint GENERIC_WRITE = 0x40000000;
        public const uint FILE_SHARE_READ = 0x00000001;
        public const uint FILE_SHARE_WRITE = 0x00000002;
        public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
        public const int OPEN_EXISTING = 3;

        #region Structs

        public enum DIGCF
        {
            DIGCF_DEFAULT = 0x1,
            DIGCF_PRESENT = 0x2,
            DIGCF_ALLCLASSES = 0x4,
            DIGCF_PROFILE = 0x8,
            DIGCF_DEVICEINTERFACE = 0x10
        }

        //SetupDiEnumDeviceInterfaces 识别出来的SP_DEVICE_INTERFACE_DATA结构,该结构标识满足搜索参数的接口
        public struct SP_DEVICE_INTERFACE_DATA
        {
            public int cbSize;                     //SP_DEVICE_INTERFACE_DATA结构的大小
            public Guid interfaceClassGuid;        //设备接口所属的类的GUID
            public int flags;                      //接口转态标记
            public int reserved;                   //保留,不做使用
        }

        [StructLayout(LayoutKind.Sequential)]
        public class SP_DEVINFO_DATA
        {
            public int cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
            public Guid classGuid = Guid.Empty; // temp
            public int devInst = 0; // dumy
            public int reserved = 0;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
        {
            internal int cbSize;
            internal short devicePath;
        }

        //HidD_GetAttributes的调用者使用此结构来对比查找设备
        public unsafe struct HIDD_ATTRIBUTES
        {
            public int Size;            //指定HIDD_ATTRIBUTES结构的大小(以字节为单位)
            public ushort VendorID;        //指定HID设备的供应商ID( VID )
            public ushort ProductID;       //指定HID设备的产品ID( PID )
            public ushort VersionNumber;   //指定HIDClass设备的制造商版本号
        }

        //自定义的结构体,用来存放自己要操作的设备信息
        public unsafe struct my_usb_id
        {
            public ushort my_vid;
            public ushort my_Pid;
            public ushort my_number;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct HIDP_CAPS
        {
            public ushort UsagePage;                                            //指定顶级集合的使用情况
            public uint Usage;                                                //指定顶级集合的 使用ID
            public ushort InputReportByteLength;                                //指定所有输入报告的最大大小(以字节为单位)
            public ushort OutputReportByteLength;                               //指定所有输出报告的最大大小(以字节为单位)
            public ushort FeatureReportByteLength;                              //指定所有功能报告的最大长度(以字节为单位)
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]               //保留供内部系统使用数组
            public ushort NumberLinkCollectionNodes;                            //指定的数量HIDP_LINK_COLLECTION_NODE了为这个顶级集合返回的结构HidP_GetLinkCollectionNodes
            public ushort NumberInputButtonCaps;                                //指定HidP_GetButtonCaps返回的输入HIDP_BUTTON_CAPS结构的数量
            public ushort NumberInputValueCaps;                                 //指定HidP_GetValueCaps返回的输入HIDP_VALUE_CAPS结构的数量
            public ushort NumberInputDataIndices;                               //指定分配给所有输入报告中的按钮和值的数据索引数
            public ushort NumberOutputButtonCaps;                               //指定HidP_GetButtonCaps返回的输出HIDP_BUTTON_CAPS结构的数量
            public ushort NumberOutputValueCaps;                                //指定HidP_GetValueCaps返回的输出HIDP_VALUE_CAPS结构的数量
            public ushort NumberOutputDataIndices;                              //指定分配给所有输出报告中的按钮和值的数据索引数
            public ushort NumberFeatureButtonCaps;                              //指定HidP_GetButtonCaps返回的功能HIDP_BUTTONS_CAPS结构的总数
            public ushort NumberFeatureValueCaps;                               //指定HidP_GetValueCaps返回的功能HIDP_VALUE_CAPS结构的总数
            public ushort NumberFeatureDataIndices;                             //指定分配给所有要素报告中的按钮和值的数据索引数
        }

        /*异步通信用,internal是错误码,internalHigh是传输字节,这个两个是IO操作完成后需要填写的内容*/
        [StructLayout(LayoutKind.Sequential)]
        public struct OVERLAPPED
        {
            public IntPtr Internal;         //I/O请求的状态代码
            public IntPtr InternalHigh;     //传输I/O请求的字节数
            public int Offset;              //文件读写起始位置
            public int OffsetHigh;          //地址偏移量
            public IntPtr hEvent;           //操作完成时系统将设置为信号状态的事件句柄
        }

        #endregion

        #region WindosApi

        //获得USB设备的GUID
        [DllImport("hid.dll")]
        public static extern void HidD_GetHidGuid(ref Guid HidGuid);

        //获得一个包含全部HID信息的结构数组的指针,具体配置看函数说明:https://docs.microsoft.com/zh-cn/windows/desktop/api/setupapi/nf-setupapi-setupdigetclassdevsw
        [DllImport("setupapi.dll", SetLastError = true)]
        public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr HwndParent, DIGCF Flags);

        //该结构用于识别一个HID设备接口,获取设备,true获取到
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

        // 获得一个指向该设备的路径名,接口的详细信息 必须调用两次 第1次返回长度 第2次获取数据 
        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData,
                                                                   int deviceInterfaceDetailDataSize, ref int requiredSize, SP_DEVINFO_DATA deviceInfoData);
        
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern Boolean SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);

        //获取设备文件(获取句柄)
        [DllImport("kernel32.dll", SetLastError = true)]
        //根据要求可在下面设定参数,具体参考参数说明:https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-createfilea
        private static extern IntPtr CreateFile
            (
             string lpFileName,                             // file name 文件名
             uint dwDesiredAccess,                        // access mode 访问模式
             uint dwShareMode,                            // share mode 共享模式
             uint lpSecurityAttributes,                   // SD 安全属性
             uint dwCreationDisposition,                  // how to create 如何创建
             uint dwFlagsAndAttributes,                   // file attributes 文件属性
             uint hTemplateFile                           // handle to template file 模板文件的句柄
            );

        [DllImport("Kernel32.dll", SetLastError = true)]  //接收函数DLL
        private static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped);

        [DllImport("kernel32.dll", SetLastError = true)] //发送数据DLL
        public static extern Boolean WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, ref uint nNumberOfBytesWrite, IntPtr lpOverlapped);
    
        [DllImport("hid.dll")]
        /*HidDeviceObject:指定顶级集合的打开句柄
          Attributes:指向调用者分配的HIDD_ATTRIBUTES结构的指针,该结构返回由HidDeviceObject指定的集合的属性*/
        private static extern Boolean HidD_GetAttributes(IntPtr hidDeviceObject, out HIDD_ATTRIBUTES HIDD_ATTRIBUTES);
       
        /**/
        [DllImport("hid.dll")]
        /*HidP_GetCaps返回一个顶级集合的 HIDP_CAPS结构,获取设备具体信息,这里暂时用不上
        PreparsedData:指向顶级集合的预分析数据的指针; Capabilities:指向调用程序分配的缓冲区的指针,该缓冲区用于返回集合的HIDP_CAPS结构*/
        private static extern uint HidP_GetCaps(IntPtr PreparsedData, out HIDP_CAPS Capabilities);
       
        [DllImport("hid.dll")]
        private static extern Boolean HidD_GetPreparsedData(IntPtr hidDeviceObject, out IntPtr PreparsedData);

        //释放设备
        [DllImport("hid.dll")]
        static public extern bool HidD_FreePreparsedData(ref IntPtr PreparsedData);

        //关闭访问设备句柄,结束进程的时候把这个加上保险点
        [DllImport("kernel32.dll")]
        static public extern int CloseHandle(IntPtr hObject);

        //查看数据传输异常函数
        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true)]
        public static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
        
        /*监听异步通信函数*/
        [DllImport("Kernel32.dll")]
        public static extern unsafe long WaitForSingleObject(IntPtr hHandle, long dwMilliseconds);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, [MarshalAs(UnmanagedType.LPStr)] string lpName);

        #endregion
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="vendorID"></param>
        /// <param name="productID"></param>
        /// <returns>HidHandle</returns>
        public IntPtr FindDevice(ushort vendorID, ushort productID)
        {
            int index = 0;
            //获取USB设备的GUID
            HidD_GetHidGuid(ref guidHID);
            //Console.WriteLine(" GUID_HID = "+ guidHID);       //输出guid信息调试用

            //获取系统中存在的所有设备的列表,这些设备已从存储卷设备接口类启用了接口
            hDevInfo = SetupDiGetClassDevs(ref guidHID, 0, IntPtr.Zero, DIGCF.DIGCF_PRESENT | DIGCF.DIGCF_DEVICEINTERFACE);
            int bufferSize = 0;
            ArrayList HIDUSBAddress = new ArrayList();

            while (true)
            {
                //获取设备,true获取到
                SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
                DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData);

                for (int i = 0; i < 3; i++)
                {
                    //识别HID设备接口,获取设备,返回true成功
                    result = SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guidHID, (UInt32)index, ref DeviceInterfaceData);
                }

                //Console.WriteLine(" 识别HID接口\t"+result);       //识别接口打印信息查看

                //第一次调用出错,但可以返回正确的Size 
                SP_DEVINFO_DATA strtInterfaceData = new SP_DEVINFO_DATA();
                //获得一个指向该设备的路径名,接口的详细信息 必须调用两次 第1次返回路径长度 
                result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, strtInterfaceData);

                //第二次调用传递返回值,调用即可成功 , 第2次获取路径数据 
                IntPtr detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
                SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
                detailData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));

                Marshal.StructureToPtr(detailData, detailDataBuffer, false);
                result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, strtInterfaceData);
                if (result == false) break;

                //获取设备路径访
                IntPtr pdevicePathName = (IntPtr)((int)detailDataBuffer + 4);
                devicePathName = Marshal.PtrToStringAuto(pdevicePathName);
                HIDUSBAddress.Add(devicePathName);
                //Console.WriteLine(" Get_DvicePathName = "+ devicePathName);     //打印路径信息,调试用

                //连接USB设备文件
                deviceHandle = CreateFile(devicePathName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);//FILE_FLAG_OVERLAPPED
                if (deviceHandle != IntPtr.Zero)                                    //设备连接成功               
                {
                    HIDD_ATTRIBUTES HIDD_ATTRIBUTE = new HIDD_ATTRIBUTES();
                    //handle是CreateFile函数返回一个有效的设备操作句柄,HIDD_ATTRIBUTES是函数返回的结构体信息(VID PID 设备号)
                    bool sel = HidD_GetAttributes(deviceHandle, out HIDD_ATTRIBUTE);
                    if (vendorID == HIDD_ATTRIBUTE.VendorID && productID == HIDD_ATTRIBUTE.ProductID) return deviceHandle;
                }
                index++;
            }
            return IntPtr.Zero;
        }

        //写设备数据
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public int Write(byte[] data)
        {
            //OVERLAPPED Overlapped = new OVERLAPPED();
            //Overlapped.hEvent = IntPtr.Zero;
            //Overlapped.Offset = 0;
            //Overlapped.OffsetHigh = 0;
            //IntPtr overLappedPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Overlapped));
            //Marshal.StructureToPtr(Overlapped, overLappedPtr, true);

            byte[] sendData = new byte[65];
            sendData[0] = 0x00;//开头处加入设备端点
            data.CopyTo(sendData, 1);
            uint read = 0;
            bool isread = WriteFile(deviceHandle, sendData, (uint)sendData.Length, ref read, IntPtr.Zero);
            if (isread == false) return Marshal.GetLastWin32Error();
            return 0;
        }

        public bool Readata(ref byte[] buffer)//USB异步接收数据
        {
            //初始化Overlapped
            //OVERLAPPED overlap = new OVERLAPPED();
            //overlap.Offset = 0;
            //overlap.OffsetHigh = 0;
            //overlap.hEvent = CreateEvent(IntPtr.Zero, false, false, null);//创建事件对象

            byte[] reBuffer = new byte[65];
            uint dwRead = 0;
            //读设备
            bool re = ReadFile(deviceHandle, reBuffer, (uint)reBuffer.Length, ref dwRead, IntPtr.Zero);
            if (re)
            {//去掉开头处的设备端点
                for (int i = 0; i < buffer.Length; i++)
                {
                    buffer[i] = reBuffer[i + 1];
                }
            }
            // long cc=WaitForSingleObject(overlap.hEvent, 5000);
            return re;
        }

        /*  释放关闭USB设备   */
        public void Dispost()
        {
            //释放设备资源(hDevInfo是SetupDiGetClassDevs获取的)
            SetupDiDestroyDeviceInfoList(hDevInfo);
            //关闭连接(HidHandle是Create的时候获取的)
            CloseHandle(deviceHandle);
            hDevInfo = IntPtr.Zero;
            deviceHandle = IntPtr.Zero;
        }
    }
View Code

调用案例如下

        private static void TestUSBHelper()
        {
            var usbHelper = new USBHelper();
            try
            {
                int errCode = 0;
                byte[] byteArr = new byte[] { 0x01, 0x02 };
               //根据vid和pid查找设备
                IntPtr handle = usbHelper.FindDevice(0x0123, 0X0002);              
                if (handle != IntPtr.Zero)
                {
                    errCode = usbHelper.Write(byteArr);
                    if (errCode == 0)
                    {
                        byte[] byteArr = new byte[5];//长度根据自定义协议定
                        bool success = usbHelper.Readata(ref byteArr);
                        if (success) Console.WriteLine("成功");
                        else Console.WriteLine("失败");
                    }             
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                usbHelper.Dispost();
            }
        }
View Code

 

posted on 2020-12-29 10:43  所谓的潇洒  阅读(452)  评论(0编辑  收藏  举报