展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > C#中的USBHID设备通信全解

C#中的USBHID设备通信全解

在C#中与USBHID设备(比如键盘、鼠标、游戏手柄)打交道,本质上是一场与Windows底层API的对话。这个过程绕不开一个关键技术:P/Invoke。它就像一座桥梁,让托管在.NET环境下的C#代码能够调用那些用C语言写成的、直接与操作系统交互的非托管函数。本文将带你走通从枚举设备、建立连接到读

本文目录

    在C#中与USBHID设备(比如键盘、鼠标、游戏手柄)打交道,本质上是一场与Windows底层API的对话。这个过程绕不开一个关键技术:P/Invoke。它就像一座桥梁,让托管在.NET环境下的C#代码能够调用那些用C语言写成的、直接与操作系统交互的非托管函数。本文将带你走通从枚举设备、建立连接到读写数据的完整路径,并提供可直接使用的代码示例。

    C#中的USBHID设备通信全解

    1. USBHID概述和C#中的实现

    USBHID,即人机接口设备,是USB标准中专门为交互设备定义的一类。相比老式的串口通信,它凭借即插即用、高传输速率和低错误率,成为了许多硬件交互的首选方案。

    要在C#世界里驾驭USBHID设备,核心在于理解其工作原理和数据流,并熟练运用P/Invoke技术来调用Windows API。这为后续所有操作——发现设备、打开连接、收发数据——奠定了基础。

    2. P/Invoke技术与Windows API调用

    2.1 P/Invoke技术简介

    2.1.1 P/Invoke技术的定义

    简单来说,P/Invoke是.NET框架提供的一种机制,允许托管代码(如C#)安全地调用非托管代码库(如Windows系统DLL中的C函数)。这使得开发者能够利用大量成熟且功能强大的Windows API,同时也为旧有Win32应用程序的迁移和维护提供了便利。

    2.1.2 P/Invoke技术的工作原理

    其核心在于桥接两个世界:受公共语言运行时(CLR)管理的托管代码,和直接在操作系统层面运行的非托管代码。P/Invoke负责处理两者之间的差异,包括内存管理、数据类型转换和异常传递,让调用过程尽可能平滑。

    2.2 Windows API在C#中的调用

    2.2.1 Windows API的搜索与选择

    动手之前,先得找到对的“工具”。你需要明确要完成什么功能,然后去查阅官方文档(如MSDN)来确定具体的API函数、它所在的DLL库、参数类型及顺序。这一步的准确性直接决定了后续代码能否成功运行。

    2.2.2 在C#中声明和使用Windows API函数

    找到API后,下一步就是在C#中声明它。这主要依靠 DllImport 属性。以下是一个调用系统消息框的经典示例:

    using System;
    using System.Runtime.InteropServices;
    
    class Program
    {
        // 使用DllImport属性指定DLL名和入口点
        [DllImport("user32.dll", SetLastError = true)]
        static extern int MessageBox(int hWnd, String text, String caption, uint type);
    
        static void Main()
        {
            // 调用MessageBox API
            MessageBox(0, "Hello World!", "My Message Box", 0);
        }
    }
    

    这段代码声明了位于 user32.dll 中的 MessageBox 函数,并指定了其参数和返回类型。这样,在C#中就可以像调用普通方法一样使用它了。

    2.3 P/Invoke技术的深入分析

    2.3.1 数据类型转换与映射

    C#和C/C++的数据类型并非总是一一对应。因此,在跨平台调用时,数据类型的正确映射至关重要。P/Invoke通过 StructLayout、MarshalAs 等属性来确保结构体布局和数据类型能够被非托管代码正确识别。

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
    
    [StructLayout(LayoutKind.Sequential)]
    struct MEMORY_BASIC_INFORMATION
    {
        public IntPtr BaseAddress;
        public IntPtr AllocationBase;
        public uint AllocationProtect;
        public IntPtr RegionSize;
        public StateEnum State;
        public AllocationType Protect;
        public TypeEnum Type;
    }
    
    // 使用时
    MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
    // ...调用VirtualAllocEx获取信息...
    VirtualQueryEx(hProcess, IntPtr.Zero, out mbi, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION)));
    

    2.3.2 异常处理与资源管理

    调用非托管代码时,错误可能发生在“墙的另一边”。P/Invoke提供了 Marshal.GetLastWin32Error() 方法来捕获这些错误。同时,必须谨慎管理非托管资源(如文件句柄、内存指针),通常使用 try-finally 或 using 模式来确保资源被正确释放,避免内存泄漏。

    IntPtr handle = IntPtr.Zero;
    try
    {
        handle = CreateFile("somefile.txt", GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
        if (handle == INVALID_HANDLE_VALUE)
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new IOException("Unable to open file", errorCode);
        }
        // ...使用文件...
    }
    finally
    {
        if (handle != IntPtr.Zero && handle != INVALID_HANDLE_VALUE)
        {
            CloseHandle(handle);
        }
    }
    

    以上示例确保了无论文件操作是否成功,句柄最终都会被安全关闭。

    3. 枚举USBHID设备的步骤

    3.1 USBHID设备枚举概念

    3.1.1 枚举设备的重要性

    枚举,简单说就是“发现并认识”设备的过程。对于USBHID设备,这是通信的第一步。主机通过枚举获取设备的供应商ID、产品ID、设备类等关键信息,没有这一步,后续的打开和读写都无从谈起。

    3.1.2 枚举过程中的关键技术点

    枚举过程依赖于一系列Windows API,如 SetupDiGetClassDevs、SetupDiEnumDeviceInterfaces 等。核心在于使用正确的设备接口GUID(全局唯一标识符)来定位HID设备,并妥善处理可能出现的错误,例如设备未连接或驱动异常。

    3.2 枚举USBHID设备的操作步骤

    3.2.1 使用Windows API枚举设备

    枚举通常从获取设备信息集开始。以下代码展示了如何遍历系统中所有已连接的HID设备:

    using System;
    using System.Runtime.InteropServices;
    
    class UsbHidEnumeration
    {
        const int DIGCF_PRESENT = 0x2;
        const int DIGCF_DEVICEINTERFACE = 0x10;
    
        [DllImport("setupapi.dll", SetLastError = true)]
        static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, IntPtr hwndParent, int flags);
    
        [DllImport("setupapi.dll", SetLastError = true)]
        static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, IntPtr deviceInfoData, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
    
        static Guid HidGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
    
        public static void EnumerateHidDevices()
        {
            IntPtr deviceInfoSet = SetupDiGetClassDevs(ref HidGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    
            SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
            deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData);
    
            for (int memberIndex = 0; SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref HidGuid, memberIndex, ref deviceInterfaceData); memberIndex++)
            {
                // 处理每一个找到的设备接口
            }
        }
    
        [StructLayout(LayoutKind.Sequential)]
        struct SP_DEVICE_INTERFACE_DATA
        {
            public int cbSize;
            public Guid InterfaceClassGuid;
            public int Flags;
            public IntPtr Reserved;
        }
    }
    

    代码首先通过 SetupDiGetClassDevs 获取HID设备列表,然后循环调用 SetupDiEnumDeviceInterfaces 遍历每个设备接口。

    3.2.2 枚举设备时的常见问题及解决方案

    枚举失败通常有几个原因:驱动问题、权限不足或物理连接故障。对应的解决思路也很直接:

    • 驱动问题:确保设备驱动程序已正确安装且为最新版本。
    • 权限问题:尝试以管理员身份运行程序。
    • 连接问题:检查USB线缆和端口,尝试重新插拔或更换端口。

    在代码层面,务必检查API调用的返回值,并通过 GetLastError 获取详细错误码,这是调试的关键。

    if (deviceInfoSet == IntPtr.Zero)
    {
        int errorCode = Marshal.GetLastWin32Error();
        throw new Exception("无法获取设备信息集。错误代码: " + errorCode);
    }
    

    4. 打开和关闭USBHID设备的方法

    成功枚举到设备后,下一步就是与之建立连接(打开)并在完成后妥善断开(关闭)。

    4.1 打开USBHID设备的步骤

    4.1.1 设备句柄的获取

    打开设备的本质是获取一个代表该设备的句柄。在Windows中,这通常通过 CreateFile 函数完成,尽管它的名字看起来像是用于文件操作。

    using System;
    using System.Runtime.InteropServices;
    
    class UsbHidDevice
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr CreateFile(
            string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            IntPtr lpSecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);
        // USB设备的路径和访问权限等参数
        private static readonly string DevicePath = "\\.\HIDI001";
        private static readonly uint GenericRead = 0x80000000;
    
        public static IntPtr OpenDevice()
        {
            IntPtr deviceHandle = CreateFile(
                DevicePath,
                GenericRead,
                0,
                IntPtr.Zero,
                0x3, // OPEN_EXISTING
                0,
                IntPtr.Zero);
            if(deviceHandle == IntPtr.Zero || deviceHandle == new IntPtr(-1))
            {
                throw new System.ComponentModel.Win32Exception();
            }
    
            return deviceHandle;
        }
    }
    

    4.1.2 打开设备时的异常处理

    打开操作可能因设备被占用、路径错误或权限问题而失败。使用 try-catch 块来捕获并处理这些异常是保证程序健壮性的必要措施。

    try
    {
        IntPtr deviceHandle = UsbHidDevice.OpenDevice();
        // 使用设备句柄进行后续操作
    }
    catch (System.ComponentModel.Win32Exception ex)
    {
        // 处理异常情况,例如用户没有足够的权限或设备不存在
        Console.WriteLine($"Error opening device: {ex.Message}");
    }
    

    4.2 关闭USBHID设备的策略

    4.2.1 设备关闭的条件和时机

    一个良好的习惯是:在程序结束或不再需要设备时,立即关闭其句柄以释放系统资源。通常放在 finally 块或 Dispose 方法中执行。

    // 在程序结束前关闭设备句柄
    if(deviceHandle != IntPtr.Zero)
    {
        CloseHandle(deviceHandle);
    }
    

    4.2.2 关闭设备前的清理工作

    关闭前,应确保所有进行中的数据操作(如读写)已经完成或已被妥善取消。这可以避免数据丢失或设备状态异常。记录关闭日志也有助于后续的问题追踪。

    // 示例代码,确保设备已断开所有连接
    if( // 设备正在传输数据的条件判断)
    {
        // 等待数据传输完成或取消传输
    }
    

    5. 发送和接收数据的机制

    与设备建立连接后,核心任务就是数据交换。理解USBHID的数据包结构和通信模式是关键。

    5.1 数据通信的基本概念

    5.1.1 数据包的结构和格式

    USBHID设备通过“报告”来传输数据,报告的结构由设备固件中的报告描述符定义。主要分为三类:

    • 输入报告:从设备流向主机,例如键盘按下的键值、鼠标移动的坐标。
    • 输出报告:从主机发送到设备,例如设置键盘背光、控制游戏手柄震动。
    • 特征报告:用于双向配置,通常用来获取或设置设备的特定属性。

    5.1.2 数据传输的同步和异步方式

    数据传输有两种模式:

    • 同步传输:调用会阻塞当前线程,直到数据传输完成。简单直接,但可能影响程序响应。
    • 异步传输:调用后立即返回,数据传输在后台进行。效率更高,但编程模型相对复杂。

    5.2 实现数据发送和接收的策略

    5.2.1 利用Windows API实现数据通信

    在C#中,读写数据主要依靠 ReadFile 和 WriteFile 这两个API。以下是一个简化的类结构展示:

    public class HidDevice
    {
        public const int HidImports.HidGuid = 0x4d1e55b2, 0xafa6, 0x4fad, 0x9c, 0x85, 0x19, 0xb8, 0x80, 0xee, 0xca, 0x8b;
    
        [DllImport("hid.dll")]
        private static extern bool HidD_GetAttributes(IntPtr HidDeviceObject, out HidAttributes Attributes);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode,
            IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead,
            out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite,
            out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
        ...
    }
    

    5.2.2 数据传输中的错误检测与处理

    ReadFile 和 WriteFile 会返回布尔值指示成功与否。失败时,应立即调用 Marshal.GetLastWin32Error() 获取错误码。常见的错误包括设备意外移除、缓冲区不足或访问被拒绝。健全的错误处理逻辑是确保应用程序稳定性的最后一道防线。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirServer Windows版
    AirServer Windows版

    AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。