
先说我自己的场景去年做一个小工具手里一块基于 STM32 的自定义 HID 设备Linux 下用 libusb 调得飞起一到 Windows 就“未知 USB 设备”。网上教程翻了一大堆不是让手写 INF就是让去关强制签名最后折腾一下午。后来换用 Zadig 2.9前后不到两分钟驱动就能跑起来程序里 open device 一下就通了。这篇就把我从“装不上”到“一键装好”再到给自制设备补上 WCID 配置的整个过程写清楚尤其适合搞嵌入式、开发板、USB 外设调试或者经常跟 Zynq、ESP32、STM32、JLINK、STLINK 这类设备打交道的人。1. Windows 下装 libusb 驱动为什么这么折腾1.1 libusb 和 Windows 驱动之间的关系很多人第一次接触 libusb 时以为它就是一套跨平台的 USB 访问库拿到源码编译一下就能用。实际上 libusb 只是用户态的 API真正跟硬件打交道的是操作系统底层的 USB 驱动栈。Linux 和 macOS 对用户态 USB 访问的接口比较开放驱动层相对透明所以 libusb 开箱即用而 Windows 从一开始就没有把通用 USB 设备的访问权限直接交给应用程序任何非标准 USB 设备要能被用户在应用层读写必须先经过一个内核驱动。这个内核驱动可以理解成一把钥匙。Windows 自带的 HID、大容量存储、UVC 这些驱动只认特定类型的设备描述符你的设备一旦不属于这些类别系统就不知道该把设备交给谁于是给一个“未知设备”的结局。libusb 在 Windows 上要正常工作必须有下面三种驱动中的一种来接管设备WinUSB、libusbK 或 libusb-win32。Zadig 的作用就是帮你从“厂商驱动 A”或“没有驱动”切换到“能让 libusb 访问的驱动 B”。Zadig 本身不包含 libusb 库它只是利用 libwdi 把目标驱动和对应的 INF 配置临时生成好再通过 Windows 的驱动安装接口写入系统。所以实操上你只需要在界面上选对设备、选对目标驱动剩下的事情它帮你处理。这也是为什么我把这工具当成 Winnw 下 USB 调试的标配。1.2 三种候选驱动WinUSB、libusbK、libusb-win32 怎么区分Zadig 的驱动下拉框里有三个选项很多新手到这里就卡住了因为名字长得像不知道选哪个。我直接给个对比表这个表基本能覆盖绝大多数应用场景。驱动来源签名情况特点适用场景WinUSBMicrosoft 官方微软签名系统自带Windows 8/10/11 上免额外安装稳定支持批量/中断/同步传输绝大多数 libusb 项目、USB 设备调试libusbKlibusbK 项目有签名版本带内核级过滤能力端点操作更灵活兼容老旧系统对老设备、特殊管道、过滤驱动有需求时libusb-win32libusb-win32 项目签名较老最早期的方案API 偏旧维护少老项目兼容、XP/旧系统之选我的经验是新项目优先选 WinUSB。一方面微软官方驱动已经预置在系统里Zadig 安装时写入一个驱动实例就行后续重装系统只要设备描述符没变照样能识别另一方面 WinUSB 在 Windows 8 之后对批量传输的支持很完整libusb 1.0 在 Windows 后端默认也是优先走 WinUSB。1.3 手写 INF 的坑和 Zadig 的定位传统方式给 USB 设备装 libusb 驱动要自己写 INF 文件。INF 里的USB\VID_xxxxPID_yyyy必须和设备描述符严格匹配少写一个MI_00都可能导致驱动装不上。还要处理 64 位系统的Signature段、管理员权限、驱动签名验证稍不留神就是“元素的 INF 中的服务安装段落无效”之类的报错。Zadig 把这条链路完全自动化了它读取设备当前的 VID/PID动态生成 INF再调用系统安装 API 安装驱动。整个过程它帮你避开了绝大多数手写 INF 时容易犯的低级错误。Zadig 的定位不是万能的驱动管家它只管“把设备切换到 WinUSB/libusbK/libusb-win32 之一”这一件事。遇到需要自定义DeviceInterfaceGUID或者设备需要特殊过滤驱动的场景还得自己动手补 INF 或改固件后面第 3 部分会讲到。2. Zadig 2.9 替换驱动前需要搞懂的界面和前置条件2.1 启动前的系统签名与杀毒软件问题很多人一听说装非官方驱动第一反应是“是不是要先禁用驱动签名强制”。实测下来Zadig 2.9 在默认开启 Secure Boot 的 Windows 10/11 上直接安装 WinUSB 或 libusbK 签名版通常没有问题。原因很简单WinUSB 是微软自家驱动签名链完全可信libusbK 官方提供的版本也有交叉签名。所以日常使用不需要关闭 Secure Boot也不建议为了装个调试驱动就把系统保护降级。杀毒软件倒是比签名更常见的拦路虎。Zadig 运行时会释放驱动文件到临时目录并请求系统驱动安装服务某些安全软件会把这种行为当成“可疑驱动篡改”拦下来。遇到过几次设备管理器里显示“Windows 无法验证此设备所需驱动程序的数字签名”但文件其实没问题就是杀毒软件把 Zadig 释放的临时 INF 标记了。遇到这种情况先临时关闭实时防护安装完再打开比关系统签名安全得多。2.2 设备枚举为什么下拉框是空的以及 List All Devices 的作用Zadig 主界面打开后如果下拉框里看不到目标设备第一件事是勾选菜单栏的Options - List All Devices。Zadig 默认只列出它认为可被替换驱动的设备也就是当前有驱动或能被识别的 USB 设备对于已经被系统识别为“未知设备”或者处于异常状态的对象它默认不显示。勾选之后所有 USB 设备都会出现包括 HID、蓝牙、内置摄像头等这时再根据 VID/PID 找到你的目标设备。2.3 替换操作的最简流程Zadig 的使用流程不复杂但每一步都有坑。我这里列一个我实际验证过的最简流程把设备插到电脑上打开设备管理器确认设备出现在哪一类下记下它的 VID 和 PID。打开 Zadig勾选Options - List All Devices在下拉框里找到对应设备。确认右侧显示的 VID/PID 和设备管理器里一致同时确认USB ID那栏不是空的。在目标驱动下拉框里选择WinUSB。点击Replace Driver如果设备还未安装过驱动则显示Install Driver。等待进度条跑完看到 “Driver installed successfully” 弹窗后拔插一次设备。这里最容易被忽略的是第 3 步。有次我帮朋友装一个国产 JTAG 工具Zadig 里下拉框显示的是另一个同 VID 但不同 PID 的设备一没注意就点了安装结果把系统自带的 USB 输入设备驱动给换了鼠标瞬间失灵。所以操作前一定逐个核对 VID/PID别只听名称。拔插一次设备这步也不是玄学。驱动替换后设备栈需要重新枚举才能加载新的驱动Windows 的即插即用服务有时候不会立刻刷新直接拔插是最快的方式。2.4 Zadig 2.9 相比旧版的体验提升Zadig 2.9 和早期版本比界面逻辑基本相同但在我实际使用中有几点明显改观。第一是新设备支持列表更新得比较勤手头有些相对冷门的 VID/PID2.4 版本里识别不出名字2.9 里能直接显示设备名称第二是驱动安装失败后的错误提示更明确不再是干巴巴的 code 代码而会给出可能的原因比如设备正在被其他程序占用或者 INF 签名异常第三是命令行的参数兼容性更好脚本批量安装时不容易出现空格路径解析问题。并不是说 2.9 改了翻天覆地的东西但对日常批量刷机和产线部署来说这些细节能省不少事。如果你还在用很老的 Zadig 版本建议直接升级到 2.9尤其是在 Windows 11 上老版本偶发的设备枚举不到问题在 2.9 上基本没再遇到。3. WCID 配置技巧让设备插上就能自动匹配 WinUSB3.1 WCID 的触发机制0xEE 字符串描述符Zadig 解决的是“设备已经插在电脑上手动给它换成 WinUSB 驱动”的问题。但如果你做的是自己设计或需要批量交付给用户的 USB 设备更理想的状态是设备一插上 Windows 就自动识别为 WinUSB不需要用户打开 Zadig 手动操作。这就是 WCID 要做的事WCID 全称 Windows Compatible ID它利用 Windows 的 Microsoft OS Descriptor 机制实现免安装驱动。Windows 枚举 USB 设备时会尝试读取字符串索引0xEE的描述符。如果设备固件在这个索引上返回特定的标识符比如MSFT100Windows 就会认为该设备支持 Microsoft OS Descriptor然后继续请求扩展属性描述符。扩展属性里可以声明一个 Compatible ID 为WINUSBWindows 看到这个值就会自动为该设备加载 WinUSB 驱动整个过程不需要 INF 文件也不需要 Zadig。这就能解释一个常见误区Zadig 不会把 WCID 写进你的设备它改的是 Windows 主机端的驱动配置。要让“插上就自动用 WinUSB”这个行为成立必须由设备固件主动支持 WCID或者通过其他软件手段在主机端做一个等价绑定。3.2 固件端最小实现以最常见的 STM32 USB 设备固件为例要让设备支持 WCID至少要做三件事在描述符请求处理函数里响应0xEE字符串描述符写入MSFT100标识再准备一份扩展属性描述符声明兼容 ID 为WINUSB并指定一个DeviceInterfaceGUID。下面是描述符响应的一个最小化示意代码简化了标准库的具体回调写法但核心逻辑不变static const uint8_t os_string_desc[] { 0x12, // bLength 18 0x03, // bDescriptorType 3 (String) M, 0x00, S, 0x00, F, 0x00, T, 0x00, 1, 0x00, 0, 0x00, 0, 0x00, 0x00, 0x00 }; // 在 USB 控制请求解析函数中当 wIndex 0xEE 时返回 os_string_desc扩展属性描述符部分要按 Microsoft OS 1.0 格式组织里面最关键的是Compatible ID WINUSB\0\0以及一个自定义的DeviceInterfaceGUID。GUID 可以在guidgen.exe里生成比如{a5dcbf10-6530-11d2-901f-00c04fb951ed}这个经典示例但量产设备建议自己单独生成避免和别人的冲突。很多开发板固件库里已经内置了 WCID 模板比如 STM32 的 USB Device 库或 TinyUSB直接开启MS OS Descriptor支持即可。如果你第一次做建议先用逻辑分析仪或 USBPcap 抓包确认主机是否真的发起了0xEE请求再检查返回的描述符长度和内容。3.3 不想改固件时软件侧等价方案并不是所有设备都能改固件有时候手里就是一个现成的外设只想让它固定使用 WinUSB这种情况下可以通过注册表写一个兼容 ID 的绑定达到“类 WCID”的效果。Zadig 安装驱动时本质上就是往设备实例的注册表键里写入了CompatibleIDs和DeviceInstanceId等相关信息让 Windows 把设备绑定到 WinUSB。手动操作时可以打开设备管理器在目标设备上选择“更新驱动程序”再选择“从计算机中查找驱动”然后从已有驱动列表里选WinUSB Device。这比用 Zadig 更保守不会碰其他设备。但要注意这种方式的绑定只对当前这台电脑生效不能随设备本身移动和固件 WCID 有本质区别。3.4 验证是否真正走了 WinUSB配置完 WCID 或者用 Zadig 换完驱动后怎么确认设备真的在 WinUSB 下工作我一般两个步骤验证打开设备管理器展开“通用串行总线设备”看目标设备对应的驱动是否是WinUSB。如果是“通用串行总线控制器”里多了USB Composite Device或者WinUSB Device就说明绑定成功。写一个 libusb 小程序用libusb_get_device_list()看看能不能拿到设备句柄能 open 就说明用户态访问通路已经打通。最直观的方式是直接用 Zadig 再看一眼如果设备当前驱动那一栏显示的是WinUSB (v6.x.x.x)说明已经没问题了。4. 常见设备驱动选型实测ESP32、STM32、FT232 这些设备怎么选4.1 USB-UART 桥接芯片不要乱换FT232、CH340、CP2102 这类 USB 转串口芯片出厂默认驱动是虚拟串口驱动Windows 识别为 COM 口。很多人想把它们换成 WinUSB 来做 libusb 直接通信我强烈不建议。原因很简单这些芯片的固件功能就是 USB-UART 桥枚举描述符里的接口协议是 CDC ACM换掉串口驱动后串口功能会失效而且波特率、流控这些参数全都没了。如果你的目标是绕过 USB-UART 桥直接和主控通信正确做法是让主控本身做 USB 设备例如 STM32 的 USB 外设手动枚举成 Vendor Class 或自定义 HID再用 Zadig 切换到 WinUSB。像 JLINK、STLINK 这类仿真器同样不建议乱换驱动因为它们自带调试协议栈Zadig 装 WinUSB 后调试工具会直接连不上目标芯片。4.2 原生 USB 设备ESP32、STM32、Zynq 这些才是主场适合用 Zadig 装 WinUSB 的设备基本都是未定义标准协议类的原生 USB 外设。ESP32 的 USB-OTG、STM32 的 USB Device 自定义类、Zynq 裸机 USB 通信方案里常见的 Vendor Class 设备都属于这一类。它们枚举出来没有现成的 Windows 驱动对应系统要么显示为未知设备要么显示为 HID 兼容设备但没有可用的用户态接口。以 ESP32 为例如果用 ESP-IDF 的 TinyUSB 库把设备配置成 Vendor ClassWindows 默认不认识这时候打开 Zadig 选 WinUSB几秒钟后 libusb 程序就能识别到设备。Zynq 裸机 USB 通信方案更是如此Vivado 里做完 USB 外设后Windows 端驱动缺失是常态用 Zadig 切到 WinUSB 再配合 libusb 上位机调试链路就很完整。4.3 我按设备类型给出一份选型建议设备/类型推荐驱动理由STM32F4/F7 自定义 Vendor ClassWinUSB批量传输稳定系统自带ESP32-S2/S3 TinyUSB VendorWinUSB官方例程也建议 WinUSBZynq 裸机 USB 外设WinUSB 或 libusbK看上位机是否用到同步传输老式 USB 数据采集卡libusbK有些老设备对 libusb-win32 的端点调度兼容差自制 HID 转普通 USB 调试设备WinUSB避免 HID 报告描述符限制选型不是越复杂越好WinUSB 能解决的问题尽量不要引入 libusbK。只有当设备有多个备用接口、需要设置接口策略或者用到 Windows 默认 WinUSB 不支持的某些控制请求时才值得考虑 libusbK。我在 Zynq 方案上曾经为了做等时传输选过 libusbK后来发现 WinUSB 在 Windows 10 之后对等时支持也够用代码反而更简洁。5. 翻车现场的恢复流程和命令行批量部署5.1 识别翻车设备管理器里的几种典型表现Zadig 用多了总会遇到翻车。最常见的是把某个设备驱动替换错了比如原先是 USB 摄像头的驱动被换成了 WinUSB导致摄像头应用打不开其次是换了驱动后 Windows 提示“设备无法启动”或者“该设备无法找到足够资源可供其使用”。这时候不要慌先判断到底是设备固件问题还是驱动问题。驱动问题通常有几个特征设备管理器里设备名称变了、前面有黄色感叹号、查看属性时错误代码是 Code 10 或 Code 28。如果设备在 Linux 或另一台电脑上能正常工作基本可以确定是 Windows 这边驱动装乱了和固件无关。5.2 用 Zadig 恢复原驱动或交给 Windows 回退恢复过程相当简单但很多人不知道。Zadig 在替换驱动之前其实并不保留原驱动文件它只是修改了设备实例的当前驱动绑定。想恢复多半还是用 Zadig 把目标驱动再换回原来的驱动类型。具体做法打开 Zadig勾选List All Devices找到目标设备下拉框里选择usbser、hidusb或具体的厂商驱动。如果不知道该选哪个最稳妥的办法是在设备管理器里“更新驱动程序”选择“自动搜索”Windows 会尝试安装它认为匹配的驱动。要是设备原来有自己品牌的安装包直接重新跑一遍官方驱动安装器通常也能把驱动改回来。如果替换后设备在 Zadig 里都看不到了或者系统连设备类型都没法识别那就拔掉设备打开“设备管理器 - 查看 - 显示隐藏的设备”在“通用串行总线设备”和“未知设备”里找残留项右键卸载同时勾选“删除此设备的驱动程序软件”然后重新插电让系统从头枚举。这个办法能解决九成以上的识别异常。5.3 命令行批量安装和脚本化部署Zadig 2.9 支持命令行参数这对产线部署和团队批量调机器特别有用。核心参数是--vid、--pid、--driver和--install比如zadig-2.9.exe --vid 0x0483 --pid 0x5740 --driver 2 --install 1 --quiet--driver后面的数字对应驱动类型一般0是 libusb-win321是 libusbK2是 WinUSB。--install 1表示安装--quiet是静默模式。实际版本可能有细微差异用之前先执行zadig-2.9.exe --help确认一下。批量部署时可以先让设备管理器刷新枚举完毕再执行命令加一点延时避免出现设备还没就绪导致找不到 VID/PID 的问题。5.4 几条实操经验总结最后说几条这些年攒下的经验都是踩过坑才得到的替换驱动前先给设备记下原始 VID/PID最好截图留底翻车后恢复能少走弯路。在测试机上先验证不要一上来就在主力开发机上装尤其不要把键盘、鼠标这类输入设备的驱动误换掉。设备固件里如果打算支持 WCID一定要自己定义独立的DeviceInterfaceGUID不要直接用网上抄来的示例 GUID否则同型号设备多了会混乱。遇到 Zadig 提示“请拔下所有设备后重试”之类的情况把不相关的 USB 设备先拔掉尤其是 U 盘和移动硬盘避免枚举列表里出现多个同 VID/PID 的复合设备导致选错。WinUSB 驱动安装成功后如果 libusb 程序还是打不开设备多半是设备权限问题检查程序是否以管理员身份运行或者设备是否被系统识别为复合设备需要同时替换父设备和子设备。Zadig 2.9 配合 WCID 这套方案是我目前在 Windows 上做 USB 调试最顺手的组合。一次把驱动换好固件端再补上 WCID 声明后面重新插拔、换电脑都不用再碰驱动工具。如果你现在正被 Windows 的 libusb 驱动问题卡住按上面的流程走一遍应该能少走不少弯路。