C# 访问USB(HID)设备

简介: 原文:C# 访问USB(HID)设备二话不说,直接给代码,如果您真想做这方面的东西,还是稍微研究下,没有现成的好类用,就需要自己了解其原理 //引用空间using System;using System.
原文: 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 了. 

目录
相关文章
|
PyTorch API C#
【Python+C#】手把手搭建基于Hugging Face模型的离线翻译系统,并通过C#代码进行访问
目前翻译都是在线的,要在C#开发的程序上做一个可以实时翻译的功能,好像不是那么好做。而且大多数处于局域网内,所以访问在线的api也显得比较尴尬。于是,就有了以下这篇文章,自己搭建一套简单的离线翻译系统。以下内容采用python提供基础翻译服务+ C#访问服务的功能,欢迎围观。
887 0
【Python+C#】手把手搭建基于Hugging Face模型的离线翻译系统,并通过C#代码进行访问
|
3月前
|
程序员 C#
C# 面向对象编程进阶:构造函数详解与访问修饰符应用
构造函数是一种特殊的方法,用于初始化对象。构造函数的优势在于,在创建类的对象时调用它。它可以用于为字段设置初始值
46 1
|
8月前
|
监控 安全 开发工具
Baumer工业相机堡盟工业相机如何通过BGAPI SDK使用DeviceEvents“设备事件”记录相机事件的各类时间(C#)
Baumer工业相机堡盟工业相机如何通过BGAPI SDK使用DeviceEvents“设备事件”记录相机事件的各类时间(C#)
63 0
|
9月前
|
C#
C#访问修饰符
C#访问修饰符
33 0
|
9月前
|
C#
C#视频—访问修饰符
C#视频—访问修饰符
C#学习—访问修饰符和类型(静态)
C#学习—访问修饰符和类型(静态)
C#学习—访问修饰符和类型(静态)
C# 访问 C DLL
C# 访问 C DLL
60 0
C#基础访问修饰符概述
C#基础访问修饰符概述
|
C#
C# 成员默认访问权限(public、private、protected、internal)
1.C#五个修饰符的权限修饰符    级别    适用成员        解释public    公开    类及类成员的修饰符   对访问成员没有级别限制private    私有    类成员的修饰符     只能在类的内部访问protected   受保护的  类成员的修饰符     ...
2665 0
|
物联网 C#
.NET平台设备使用C#语言接入阿里云IoT
1. 准备工作 1.1 注册阿里云账号 使用淘宝账号或手机号,开通阿里云账号,并通过实名认证(可以用支付宝认证) 1.2 免费开通IoT物联网套件 产品官网 https://www.aliyun.com/product/iot 1.
5323 1