0

编辑:这个问题随着时间的推移而演变。基本问题是关于如何在 Windows 中连接和读取/写入 USB 设备。最终我在@benvoigt 的帮助下回答了这个问题。

我编写了一个 Hid 库,它可以向 Hid USB 设备写入和读取数据。它运作良好。但是,我连接的设备已从隐藏访问切换到普通 USB。连接到不同类型的设备时,Hid 代码不起作用。我现在的目标是连接到 USB 接口(与 Hid 接口相反)并对其进行读/写。

在所有访问 USB 的平台上,我们必须查询 USB 设备存在的接口,然后“声明”一个用于读写的接口。这是来自 LibUsbDotNet 示例的一些代码(LibUsb 是一个有效的 C USB 库,LibUsbDotNet 包装了它)https://github.com/LibUsbDotNet/LibUsbDotNet/blob/master/src/Examples/Read.Write/ReadWrite.cs

            using (var context = new UsbContext())
            {
                context.SetDebugLevel(LogLevel.Info);

                //Get a list of all connected devices
                var usbDeviceCollection = context.List();

                //Narrow down the device by vendor and pid
                var selectedDevice = usbDeviceCollection.FirstOrDefault(d => d.ProductId == ProductId && d.VendorId == VendorId);

                //Open the device
                selectedDevice.Open();

                //Get the first config number of the interface
                selectedDevice.ClaimInterface(selectedDevice.Configs[0].Interfaces[0].Number);

                //Open up the endpoints
                var writeEndpoint = selectedDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
                var readEnpoint = selectedDevice.OpenEndpointReader(ReadEndpointID.Ep01);

                //Create a buffer with some data in it
                var buffer = new byte[64];
                buffer[0] = 0x3f;
                buffer[1] = 0x23;
                buffer[2] = 0x23;

                //Write three bytes
                writeEndpoint.Write(buffer, 3000, out var bytesWritten);

                var readBuffer = new byte[64];

                //Read some data
                readEnpoint.Read(readBuffer, 3000, out var readBytes);
            }
}

我有一种感觉,LibUsb 正在像这样(https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2199)实现 C 中接口/端点的开放。这是它调用初始化的地方:https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2225这是我的代码失败的地方。

一小段信息是,这绝对是一个 WinUSB 设备。我可以在这里看到:

在此处输入图像描述

根据其他人的评论和示例代码,我可以看到我需要使用 winusb.dll。我可以调用 CreateFile 从设备获取句柄。根据我看到的其他示例代码,下一步是调用 WinUsb_Initialize。但是,当我调用它时,我得到一个错误代码 8 (ERROR_NOT_ENOUGH_MEMORY)。这里有一些信息https://docs.microsoft.com/en-us/windows/desktop/api/winusb/nf-winusb-winusb_initialize。但是,我不太明白它要求我做什么。到目前为止,这是我的代码:

    public override async Task InitializeAsync()
    {
        Dispose();

        if (string.IsNullOrEmpty(DeviceId))
        {
            throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
        }

        _DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);

        var errorCode = Marshal.GetLastWin32Error();

        if (errorCode > 0) throw new Exception($"Write handle no good. Error code: {errorCode}");

        if (_DeviceHandle.IsInvalid) throw new Exception("Device handle no good");

        var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var interfaceHandle);

        errorCode = Marshal.GetLastWin32Error();

        if (!isSuccess) throw new Exception($"Initialization failed. Error code: {errorCode}");

        IsInitialized = true;

        RaiseConnected();
    }

您可以在此处克隆此 repo 的分支: https ://github.com/MelbourneDeveloper/Device.Net/tree/WindowsUsbDevice 。只需运行 Usb.Net.WindowsSample 项目。

我也试过这个并得到完全相同的结果:

public override async Task InitializeAsync()
{
    Dispose();

    if (string.IsNullOrEmpty(DeviceId))
    {
        throw new WindowsException($"{nameof(DeviceDefinition)} must be specified before {nameof(InitializeAsync)} can be called.");
    }

    _DeviceHandle = APICalls.CreateFile(DeviceId, (APICalls.GenericWrite | APICalls.GenericRead), APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, APICalls.FileAttributeNormal | APICalls.FileFlagOverlapped, IntPtr.Zero);

    var errorCode = Marshal.GetLastWin32Error();

    if (errorCode > 0) throw new Exception($"Write handle no good. Error code: {errorCode}");

    var interfaceHandle = new IntPtr();

    var pDll = NativeMethods.LoadLibrary(@"C:\GitRepos\Device.Net\src\Usb.Net.WindowsSample\bin\Debug\net452\winusb.dll");

    var pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "WinUsb_Initialize");

    var initialize = (WinUsb_Initialize)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(WinUsb_Initialize));

    var isSuccess = initialize(_DeviceHandle, ref interfaceHandle);

    errorCode = Marshal.GetLastWin32Error();

    if (!isSuccess) throw new Exception($"Initialization failed. Error code: {errorCode}");

    IsInitialized = true;

    RaiseConnected();
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle);

我坚信设备的 WinUSB 实现本身存在问题。它适用于 LibUsb、UWP 和 Android 等其他库,但 WinUsb 似乎不喜欢它。我尝试了这个库:https ://github.com/madwizard-thomas/winusbnet ,它在同一个调用中也失败了,错误代码相同。它失败并获得错误代码 8 的行在这里:https ://github.com/madwizard-thomas/winusbnet/blob/8f62d751a99be1e31d34b91115715d60aeff​​2dfc/WinUSBNet/API/WinUSBDevice.cs#L225

我的代码有什么问题?我需要做些什么来为 WinUsb_Initialize 调用分配内存吗?

我应该如何使用 winusb.dll Windows API?我需要进行哪些 API 调用来声明和接口或端点以进行读写?

如果有人可以向我指出一个简单的 C 或 C# 示例,它可以读取和写入 USB 设备并实际工作,那真的会对我有所帮助

WinDBG 输出:

************* 路径验证摘要 ************** 响应时间 (ms) 位置 Deferred
srv* 符号搜索路径为:srv* 可执行搜索路径为: ModLoad: 00000236 157c0000 00000236157c8000 Usb.Net.WindowsSample.exe ModLoad: 00007ffb 62880000 00007ffb62a61000 ntdll.dll ModLoad: 00007ffb 60f40000 00007ffb610d0000 C:\WINDOWS\System32\user32.dll ModLoad: 00007ffb 5ed00000 00007ffb5ed20000
C:\WINDOWS\System32\007ffbffbx34e1b0000 00007ffb 4e214000 C:\WINDOWS\SYSTEM32\MSCOREE.DLL ModLoad: 00007ffb 612a0000 00007ffb612c8000 C:\WINDOWS\System32\GDI32.dll onecore\windows\core\console\open\src\renderer\gdi\invalidate.cpp(121)\conhost。 exe!00007FF7169FE2AF: (caller: 00007FF7169FF414) ReturnHr(1) tid(4230) 80070578 无效的窗口句柄。模组加载:00007ffb60990000 00007ffb60a42000
C:\WINDOWS\System32\KERNEL32.dll ModLoad: 00007ffb 5f000000 00007ffb5f192000 C:\WINDOWS\System32\gdi32full.dll ModLoad: 00007ffb 60d90000 00007ffb60f03000 C:\WINDOWS\System32\MSCTF.dll ModLoad: 00007ffb 5ed80000 00007ffb5eff3000
C:\WINDOWS\ System3 KERNELBASE.dll ModLoad: 00007ffb 60f3d000 C:\WINDOWS\System32\IMM32.DLL60610000 00007ffb606d2000 C:\WINDOWS\System32\OLEAUT32.dll ModLoad: 00007ffb60f10000 00007ffb

************* 路径验证摘要 ************** 响应时间 (ms) 位置 Deferred
srv* 符号搜索路径为:srv* 可执行搜索路径为: ModLoad: 00007ff7 169f0000 00007ff716a8f000 conhost.exe ModLoad: 00007ffb 61340000 00007ffb62780000 C:\WINDOWS\System32\shell32.dll ModLoad: 00007ffb 5cd80000 00007ffb5cda9000
C:\WINDOWS\system32\dwmapi.dll ModLoad 62880000 00007ffb: 00007ffb 5fcc0000 00007ffb62a6100000000007: WINDOWS\System32\cfgmgr32.dll ModLoad: 00007ffb5f530000 00007ffb 5fc3d000
C:\WINDOWS\System32\windows.storage.dll onecore\windows\core\console\open\src\renderer\gdi\invalidate.cpp(121)\conhost.exe! 00007FF7169FE2AF: (调用者: 00007FF7169FF414) ReturnHr(2) tid(4230) 80070578 无效的窗口句柄。模组加载:00007ffb61140000 00007ffb61191000
C:\WINDOWS\System32\shlwapi.dll ModLoad: 00007ffb 60990000 00007ffb60a42000 C:\WINDOWS\System32\KERNEL32.DLL ModLoad: 00007ffb 5ec30000 00007ffb5ec41000
C:\WINDOWS\System32\kernel.appcore.dll ModLoad: 00007ffb 5ed80000 00007ffb5eff3000 C:\WINDOWS\ System32\KERNELBASE.dll ModLoad: 00007ffb 5ec10000 00007ffb5ec2f000 C:\WINDOWS\System32\profapi.dll ModLoad: 00007ffb 5ebc0000 00007ffb5ec0c000
C:\WINDOWS\System32\powrprof.dll ModLoad: 00007ffb 5ebb0000 00007ffb5ebba000 C:\WINDOWS\System32\FLTLIB.DLL ModLoad: 00007ffb 5f490000 00007ffb5f52f000
C:\WINDOWS\System32\msvcp_win.dll ModLoad: 00007ffb 5f1a0000 00007ffb5f29a000 C:\WINDOWS\System32\ucrtbase.dll ModLoad: 00007ffb 606e0000 00007ffb60789000 C:\WINDOWS\System32\shcore.dll ModLoad: 00007ffb 4e290000 00007ffb4e4f9000
C:\WINDOWS\WinSxS\amd64_dfmicrosoft.windows.common-controls_6_165b.f6444441 .472_none_fb3f9af53068156d\comctl32.DLL ModLoad: 00007ffb 5ca60000 00007ffb5caf8000
C:\WINDOWS\system32\uxtheme.dll ModLoad: 00007ffb 608f0000 00007ffb6098e000 C:\WINDOWS\System32\msvcrt.dll ModLoad: 00007ffb601e0000 00007ffb60304000 C:\WINDOWS\System32\RPCRT4.dll ModLoad: 00007ffb 60a60000 00007ffb60d82000
C:\WINDOWS\System32\combase.dll ModLoad: 00007ffb 5fc40000 00007ffb5fcba000 C:\WINDOWS\System32\bcryptPrimitives.dll ModLoad: 00007ffb 627a0000 00007ffb62841000 C:\WINDOWS\System3 advapi32.dll ModLoad: 00007ffb 57bc6000 C:\WINDOWS\System32\TextInputFramework.dll (3d80.256c): Break 指令异常 - 代码 80000003 (第一次机会) ntdll!LdrpDoDebuggerBreak+0x30: 00007ffb`6294c93c cc int 3610d0000 00007ffb 6112b000
C:\WINDOWS\System32\sechost.dll ModLoad: 00007ffb57b30000 00007ffb

4

2 回答 2

1

以下是如何通过 WinUSB 库连接和读取/写入 USB 设备

所有这些代码都包含在 Device.Net 存储库中:https ://github.com/MelbourneDeveloper/Device.Net 。这里有一个样本。它会根据插入的设备自动在 Hid 和 UWP 之间切换。https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net.WindowsSample/Program.cs

连接并获取信息

写和读

API 调用

public static class Kernel32APICalls
{
    //Abridged

    #region Kernel32
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
    #endregion    
}

public static partial class WinUsbApiCalls
{
    public const uint DEVICE_SPEED = 1;
    public const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
    public const int WritePipeId = 0x80;

    /// <summary>
    /// Not sure where this constant is defined...
    /// </summary>
    public const int DEFAULT_DESCRIPTOR_TYPE = 0x01;

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, byte[] Buffer, uint BufferLength, ref uint LengthTransferred, IntPtr Overlapped);

    [DllImport("winusb.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool WinUsb_GetAssociatedInterface(SafeFileHandle InterfaceHandle, byte AssociatedInterfaceIndex, out SafeFileHandle AssociatedInterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, ushort LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, uint BufferLength, out uint LengthTransfered);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Free(SafeFileHandle InterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out SafeFileHandle InterfaceHandle);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, uint InformationType, ref uint BufferLength, ref byte Buffer);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryInterfaceSettings(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_QueryPipe(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_ReadPipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, byte PipeID, uint PolicyType, uint ValueLength, ref uint Value);

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_WritePipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
}

特别感谢@benvoigt 让我继续解决这个问题。

于 2018-12-28T05:53:27.590 回答
1

好吧,您在 64 字节传输中看到的怪异是众所周知的:

用短数据包分隔写传输

USB 驱动程序堆栈驱动程序在写入设备时对数据包大小施加的限制与从设备读取时施加的限制不同。一些客户端驱动程序必须频繁传输少量控制数据来管理他们的设备。在这种情况下,将数据传输限制为统一大小的数据包是不切实际的。因此,驱动程序堆栈不会为数据写入期间小于端点最大大小的数据包分配任何特殊意义。这允许客户端驱动程序将到设备的大量传输分成多个小于或等于最大值的任意大小的 URB。

驱动程序必须通过小于最大大小的数据包结束传输,或者通过零长度数据包来限制传输的结束。直到驱动程序发送一个小于 wMaxPacketSize 的数据包,传输才完成。如果传输大小是最大值的精确倍数,则驱动程序必须发送一个长度为零的定界数据包以显式终止传输

按照 USB 规范的要求,使用零长度数据包来分隔数据传输是客户端驱动程序的责任。USB 驱动程序堆栈不会自动生成这些数据包。

来自MSDN 上的USB 传输和数据包大小


至于其余的……取决于设备是否声明自己是复合设备,Windows 的驱动程序加载器将选择一个或多个设备驱动程序来连接到设备。这些驱动程序负责选择与设备上的哪个端点进行通信。因此,从用户空间,通常打开驱动程序界面并开始执行 I/O 就足够了。例如,HID 类驱动程序知道如何识别和启用 HID 端点。

由于您有使用 UWP 的东西,因此您似乎已经加载了 WinUSB 驱动程序(因为那是第 1 步)。因此,您将使用 WinUSB API 与之对话。

这是WinUSB 的 C 和 C++ API的文档。它有端点设置的例子,看起来比你引用的 libusb 代码要少得多(尽管这可能也与代码格式和样式有关)。

于 2018-12-26T21:47:39.930 回答