zoukankan      html  css  js  c++  java
  • C# 访问USB(HID)设备

    二话不说,直接给代码,如果您真想做这方面的东西,还是稍微研究下,没有现成的好类用,就需要自己了解其原理

    //引用空间
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Collections;
    using System.IO;

     //以下是调用windows的API的函数
            
    //获得GUID
            [DllImport("hid.dll")]
            public static extern void HidD_GetHidGuid(ref Guid HidGuid);
            Guid guidHID = Guid.Empty;
            //过滤设备,获取需要的设备
            [DllImport("setupapi.dll", SetLastError = true)]
            public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr HwndParent, DIGCF Flags);
            IntPtr hDevInfo;
            //获取设备,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);
            public struct SP_DEVICE_INTERFACE_DATA
            {
                public int cbSize ;
                public Guid interfaceClassGuid;
                public int flags;
                public int reserved;
            }

            // 获取接口的详细信息 必须调用两次 第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);
            [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;
            }

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

            //获取设备文件
            [DllImport("kernel32.dll", SetLastError = true)]
            private static extern int 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)]
            private static extern bool ReadFile
                (
                    IntPtr hFile,
                    byte[] lpBuffer,
                    uint nNumberOfBytesToRead,
                    ref uint lpNumberOfBytesRead,
                    IntPtr lpOverlapped
                );

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

    接下来是访问设备的代码

    //代码暂时没有整理,传入参数是设备序号,
    //有些USB设备其实有很多HID设备,就是一个接口上有几个设备,这个时候需要
    //用index++来逐个循环,直到获取设备返回false后,跳出去,把获取的设备
    //路径全记录下来就好了,我这里知道具体设备号,所以没有循环,浪费我时间

             
    //定于句柄序号和一些参数,具体可以去网上找这些API的参数说明,后文我看能不能把资料也写上去
             int HidHandle = -1;
            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 int OPEN_EXISTING = 3;

    private void UsBMethod(int index)
            {
                HidD_GetHidGuid(ref guidHID);
                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++)
                
    //{
                    bool result = SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guidHID, (UInt32)index, ref DeviceInterfaceData);
                //}
                
    //第一次调用出错,但可以返回正确的Size 
                SP_DEVINFO_DATA strtInterfaceData = new SP_DEVINFO_DATA();
                result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, IntPtr.Zero, 0ref bufferSize, strtInterfaceData);
                //第二次调用传递返回值,调用即可成功
                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);
                string devicePathName = Marshal.PtrToStringAuto(pdevicePathName);
                HIDUSBAddress.Add(devicePathName);
                //index++;
                
    //break;
                
    //}

                
    //连接设备文件
                int aa = CT_CreateFile(devicePathName);
                bool bb = USBDataRead(HidHandle);
            }

           //建立和设备的连接
           public unsafe int CT_CreateFile(string DeviceName)
            {
                HidHandle = CreateFile(
                    DeviceName,
                    GENERIC_READ,// | GENERIC_WRITE,//读写,或者一起
                    FILE_SHARE_READ,// | FILE_SHARE_WRITE,//共享读写,或者一起
                    0,
                    OPEN_EXISTING,
                    0,
                    0);
                if (HidHandle == -1)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            }
            
            //根据CreateFile拿到的设备handle访问文件,并返回数据
            public unsafe bool USBDataRead(int handle)
            {
                while (true)
                {
                    uint read = 0;
                    //注意字节的长度,我这里写的是8位,其实可以通过API获取具体的长度,这样安全点,
                      
    //具体方法我知道,但是没有写,过几天整理完代码,一起给出来
                    Byte[] m_rd_data = new Byte[8];
                    bool isread = ReadFile((IntPtr)handle, m_rd_data, (uint)8ref read, IntPtr.Zero);
                    //这里已经是拿到的数据了
                    Byte[] m_rd_dataout = new Byte[read];
                    Array.Copy(m_rd_data, m_rd_dataout, read);
                }
            }

    OK,如果只是获取USB传过来的数据,这里已经足够了,但是有点要注意,2000和XP如果要获取HID键盘和鼠标的数据,readfile是不行的,;
    在Win2000和WinXP下不能用CreateFile+ReadFile/WriteFile的方式来读写标准鼠标和标准键盘的数据,因为它们是系统独占的(Exlusive)。
    如果你是其他HID类设备,比如游戏手柄或者自定义HID设备,都可以用上面的方式来收发数据,
    怎么访问我暂时也不知道,估计要用它方法,看到有些软件是用截取的手段,估计是用钩子了吧。。


    还有获取报文长度的代码,如果不确定报文长度,或者为了驱动适应变化,就用一下代码来确定报文的长度

    //获取设备具体信息   
            [DllImport("hid.dll", SetLastError = true)]   
            private unsafe static extern int HidP_GetCaps(   
                int pPHIDP_PREPARSED_DATA,                    // IN PHIDP_PREPARSED_DATA  PreparsedData,   
                ref HIDP_CAPS myPHIDP_CAPS);                // OUT PHIDP_CAPS  Capabilities   
      
            [DllImport("hid.dll", SetLastError = true)]   
            private unsafe static extern int HidD_GetPreparsedData(   
                int hObject,                                // IN HANDLE  HidDeviceObject,   
                ref int pPHIDP_PREPARSED_DATA);           
      
            // HIDP_CAPS   
            [StructLayout(LayoutKind.Sequential)]   
            public unsafe struct HIDP_CAPS   
            {   
                public System.UInt16 Usage;                    // USHORT   
                public System.UInt16 UsagePage;                // USHORT   
                public System.UInt16 InputReportByteLength;   
                public System.UInt16 OutputReportByteLength;   
                public System.UInt16 FeatureReportByteLength;   
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]   
                public System.UInt16[] Reserved;                // USHORT  Reserved[17];               
                public System.UInt16 NumberLinkCollectionNodes;   
                public System.UInt16 NumberInputButtonCaps;   
                public System.UInt16 NumberInputValueCaps;   
                public System.UInt16 NumberInputDataIndices;   
                public System.UInt16 NumberOutputButtonCaps;   
                public System.UInt16 NumberOutputValueCaps;   
                public System.UInt16 NumberOutputDataIndices;   
                public System.UInt16 NumberFeatureButtonCaps;   
                public System.UInt16 NumberFeatureValueCaps;   
                public System.UInt16 NumberFeatureDataIndices;   
            }   
      
     int reportLength = 8;   
      
                //获取设备发送的字节的长度(也有其他信息)   
                int myPtrToPreparsedData = -1;   
                int result1 = HidD_GetPreparsedData(handle, ref myPtrToPreparsedData);   
                HIDP_CAPS myHIDP_CAPS = new HIDP_CAPS();   
                int result2 = HidP_GetCaps(myPtrToPreparsedData, ref myHIDP_CAPS);   
                reportLength = myHIDP_CAPS.InputReportByteLength;  

    再补充点,释放设备资源的时候需要用到

    //释放设备的访问
            [DllImport("kernel32.dll")]
            internal static extern int CloseHandle(int hObject);
    //释放设备
            [DllImport("setupapi.dll", SetLastError = true)]
            internal static extern IntPtr SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    public void Dispost()
            {
                //释放设备资源(hDevInfo是SetupDiGetClassDevs获取的)
                SetupDiDestroyDeviceInfoList(hDevInfo);
                //关闭连接(HidHandle是Create的时候获取的)
                CloseHandle(HidHandle);
            }


      ok 了. 

  • 相关阅读:
    代码优化:尽量减少对变量的重复计算
    Easy code数据库类型NUMBER(20),没有找到映射关系
    Apache JMeter的下载和安装
    启动报错:Error Running cant find catalina.jar
    创建自定义配置节
    Word转Chm 在线转换应用
    silverlight概述
    布局
    case of silverlight
    关于SilverLight打印(二)
  • 原文地址:https://www.cnblogs.com/xidongs/p/2266100.html
Copyright © 2011-2022 走看看