使用LibUsbDotNet向Android平板批量传输数据时遇读取失败及设备仅在无数据传输模式下被识别的问题
看起来你遇到了两个核心问题:一是Android USB模式与LibUsb枚举的兼容性,二是直接操作ADB接口导致的读取失败。我来一步步帮你拆解和解决:
先搞懂核心原因
1. 为什么只有“无数据传输”模式下设备能被识别?
当你选择MTP/PTP等数据传输模式时,Windows会自动加载系统独占驱动(比如MTP驱动),这些驱动会抢占USB设备的接口资源,导致LibUsbDotNet根本枚举不到设备。只有在“无数据传输”模式下,系统没有加载独占驱动,同时你开启了USB调试,此时设备暴露的是ADB专用USB接口——这也是为什么你的枚举代码只能在这个模式下看到设备。
但这里有个坑:ADB接口是为ADB协议设计的,它不是通用的批量传输接口,直接用LibUsb去读写它的端点肯定会报错。
2. “Read failed:ErrorPipe”是什么意思?
这个错误对应LibUsb的LIBUSB_ERROR_PIPE,常见原因包括:
- 没有正确激活设备的USB配置、打开并Claim接口
- 选错了端点(比如用了控制/中断端点,或者方向搞反了)
- 尝试读写的端点本身不支持批量传输(比如ADB的端点)
- 设备没有响应主机的请求
分步解决方案
方案一:如果一定要用LibUsb做直接批量传输
第一步:让Android进入正确的USB模式
你需要让平板进入USB配件模式(USB Accessory Mode),这是Android专门为外部设备(比如你的PC)直接通过USB与平板通信设计的模式,会暴露通用的批量传输端点。
- 开启方式:在开发者选项里找到“USB配件模式”,开启后连接PC即可。
第二步:替换USB驱动为WinUSB
即使进入了配件模式,Windows可能还是会加载默认驱动,导致LibUsb无法访问。你需要用Zadig工具将对应设备的驱动替换为WinUSB(LibUsb官方推荐的驱动)。
- 注意:替换驱动后,Windows文件管理器可能无法识别平板的存储,需要根据需求权衡。
第三步:修正你的代码,正确激活设备与接口
你的枚举代码只是打印设备信息,没有做LibUsb要求的关键操作:激活配置、打开并Claim接口。以下是修正后的核心代码片段:
public static void Main(string[] args) { UsbRegDeviceList allDevices = UsbDevice.AllDevices; // 可以先过滤Android设备的VID(通常是0x18D1,不同品牌可能有差异) foreach (UsbRegistry usbRegistry in allDevices.Where(d => d.Vid == 0x18D1)) { if (usbRegistry.Open(out MyUsbDevice)) { try { // 激活第一个USB配置(Android设备通常只有一个配置) if (MyUsbDevice.Configs.Count > 0) { var activeConfig = MyUsbDevice.Configs[0]; MyUsbDevice.SetConfiguration(activeConfig.Value); Console.WriteLine($"已激活配置: {activeConfig}"); // 遍历接口,找到支持批量传输的接口 foreach (var interfaceInfo in activeConfig.InterfaceInfoList) { using (var usbInterface = MyUsbDevice.OpenInterface(interfaceInfo.InterfaceID)) { if (usbInterface.ClaimInterface()) { Console.WriteLine($"已Claim接口: {interfaceInfo.InterfaceID}"); // 找到批量传输端点 foreach (var endpoint in interfaceInfo.EndpointInfoList) { if (endpoint.Type == UsbEndpointType.Bulk) { var direction = (endpoint.EndpointAddress & 0x80) == 0x80 ? "IN(设备→主机)" : "OUT(主机→设备)"; Console.WriteLine($"找到批量端点: {endpoint.EndpointAddress} [{direction}]"); // 尝试读取(仅当是IN端点时) if ((endpoint.EndpointAddress & 0x80) == 0x80) { var reader = usbInterface.CreateEndpointReader(endpoint.EndpointAddress); byte[] buffer = new byte[512]; int bytesRead; var error = reader.Read(buffer, 1000, out bytesRead); if (error != ErrorCode.None) { Console.WriteLine($"读取失败: {error}"); } else { Console.WriteLine($"成功读取 {bytesRead} 字节"); } } } } usbInterface.ReleaseInterface(); } } } } } catch (Exception ex) { Console.WriteLine($"操作出错: {ex.Message}"); } finally { MyUsbDevice.Close(); } } } }
方案二:更简单的替代方案(推荐)
如果你的需求只是批量传输文件,完全不需要直接操作LibUsb——用ADB协议更简单可靠,而且不需要折腾驱动和端点:
- 你可以直接在C#里调用ADB的
push命令,或者用现成的.NET库(比如SharpAdbClient)来封装ADB操作。 - 示例代码(用Process调用ADB):
using System.Diagnostics; public static void PushFileToTablet(string localPath, string remotePath) { var process = new Process(); process.StartInfo.FileName = "adb.exe"; process.StartInfo.Arguments = $"push \"{localPath}\" \"{remotePath}\""; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.Start(); string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (process.ExitCode == 0) { Console.WriteLine($"文件传输成功: {output}"); } else { Console.WriteLine($"传输失败: {error}"); } }
最后总结
如果你一定要用LibUsb做直接的USB批量传输:
- 必须让Android进入USB配件模式
- 用Zadig替换对应USB接口的驱动为WinUSB
- 确保代码正确激活配置、打开并Claim接口,选择正确的批量端点
如果只是要传输文件:
- 优先用ADB方案,不需要直接操作USB,简单可靠,支持所有开启USB调试的模式
备注:内容来源于stack exchange,提问作者Hid

