Edit: This question evolved over time. The basic question was about how to connect to a and read/write to/from a USB device in Windows. Eventually I answered the question with the help of @benvoigt.
I have written a Hid library which writes and reads to/from Hid USB devices. It works well. However, the device I am connecting to has switched from Hid access to vanilla USB. The Hid code does not work when connecting to the different type of device. My aim now is to connect to the USB interface (as opposed to the Hid interface) and read/write to/from it.
On all platforms that access USB, we have to query the interfaces that exist for the USB device, and then "claim" an interface for reading and writing. Here is some code from a LibUsbDotNet sample (LibUsb is a working C USB library and LibUsbDotNet wraps it) 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);
}
}
I have a feeling that LibUsb is achieving the opening of interfaces/endpoints in C like this (https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2199) . This is where it calls Initialize: https://github.com/libusb/libusb/blob/c6f3866414e8deeee19e8a9f10f20bde9cb408d3/libusb/os/windows_winusb.c#L2225 which is where my code is failing.
A little snippet of information is that this is definitely a WinUSB device. I can see that here:
Based on other people's comments and sample code, I can see that I need to use winusb.dll. I am able to call CreateFile to get a handle from the device. According to other sample code I have seen, the next step is to call WinUsb_Initialize. However, when I call this, I get an error code of 8 (ERROR_NOT_ENOUGH_MEMORY). There is some information here https://docs.microsoft.com/en-us/windows/desktop/api/winusb/nf-winusb-winusb_initialize . But, I don't quite understand what it is asking me to do. This is my code so far:
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();
}
You can clone the branch of this repo here: https://github.com/MelbourneDeveloper/Device.Net/tree/WindowsUsbDevice. Just run the Usb.Net.WindowsSample project.
I also tried this and got exactly the same result:
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);
I strongly believe that there is something wrong with the device's WinUSB implementation itself. It works with other libraries like LibUsb, UWP, and Android, but WinUsb doesn't seem to like it. I tried this library: https://github.com/madwizard-thomas/winusbnet and it also fails on the same call with the same error code. The line it fails on and gets error code 8 is here: https://github.com/madwizard-thomas/winusbnet/blob/8f62d751a99be1e31d34b91115715d60aeff2dfc/WinUSBNet/API/WinUSBDevice.cs#L225
What is wrong with my code here? Is there something I need to do to allocate memory for the WinUsb_Initialize call?
How should I use the winusb.dll Windows API? What API calls do I need to make to claim and interface or endpoint for reading and writing?
It would really help me if someone could point me to a simple C or C# sample that reads and writes to a USB device and actually works.
WinDBG output:
************* Path validation summary ************** Response Time (ms) Location Deferred
srv* Symbol search path is: srv* Executable search path is: ModLoad: 00000236157c0000 00000236157c8000 Usb.Net.WindowsSample.exe ModLoad: 00007ffb62880000 00007ffb62a61000 ntdll.dll ModLoad: 00007ffb60f40000 00007ffb610d0000 C:\WINDOWS\System32\user32.dll ModLoad: 00007ffb5ed00000 00007ffb5ed20000
C:\WINDOWS\System32\win32u.dll ModLoad: 00007ffb4e1b0000 00007ffb4e214000 C:\WINDOWS\SYSTEM32\MSCOREE.DLL ModLoad: 00007ffb612a0000 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 Invalid window handle. ModLoad: 00007ffb60990000 00007ffb60a42000
C:\WINDOWS\System32\KERNEL32.dll ModLoad: 00007ffb5f000000 00007ffb5f192000 C:\WINDOWS\System32\gdi32full.dll ModLoad: 00007ffb60d90000 00007ffb60f03000 C:\WINDOWS\System32\MSCTF.dll ModLoad: 00007ffb5ed80000 00007ffb5eff3000
C:\WINDOWS\System32\KERNELBASE.dll ModLoad: 00007ffb60610000 00007ffb606d2000 C:\WINDOWS\System32\OLEAUT32.dll ModLoad: 00007ffb60f10000 00007ffb60f3d000 C:\WINDOWS\System32\IMM32.DLL************* Path validation summary ************** Response Time (ms) Location Deferred
srv* Symbol search path is: srv* Executable search path is: ModLoad: 00007ff7169f0000 00007ff716a8f000 conhost.exe ModLoad: 00007ffb61340000 00007ffb62780000 C:\WINDOWS\System32\shell32.dll ModLoad: 00007ffb5cd80000 00007ffb5cda9000
C:\WINDOWS\system32\dwmapi.dll ModLoad: 00007ffb62880000 00007ffb62a61000 ntdll.dll ModLoad: 00007ffb5fcc0000 00007ffb5fd09000 C:\WINDOWS\System32\cfgmgr32.dll ModLoad: 00007ffb5f530000 00007ffb5fc3d000
C:\WINDOWS\System32\windows.storage.dll onecore\windows\core\console\open\src\renderer\gdi\invalidate.cpp(121)\conhost.exe!00007FF7169FE2AF: (caller: 00007FF7169FF414) ReturnHr(2) tid(4230) 80070578 Invalid window handle. ModLoad: 00007ffb61140000 00007ffb61191000
C:\WINDOWS\System32\shlwapi.dll ModLoad: 00007ffb60990000 00007ffb60a42000 C:\WINDOWS\System32\KERNEL32.DLL ModLoad: 00007ffb5ec30000 00007ffb5ec41000
C:\WINDOWS\System32\kernel.appcore.dll ModLoad: 00007ffb5ed80000 00007ffb5eff3000 C:\WINDOWS\System32\KERNELBASE.dll ModLoad: 00007ffb5ec10000 00007ffb5ec2f000 C:\WINDOWS\System32\profapi.dll ModLoad: 00007ffb5ebc0000 00007ffb5ec0c000
C:\WINDOWS\System32\powrprof.dll ModLoad: 00007ffb5ebb0000 00007ffb5ebba000 C:\WINDOWS\System32\FLTLIB.DLL ModLoad: 00007ffb5f490000 00007ffb5f52f000
C:\WINDOWS\System32\msvcp_win.dll ModLoad: 00007ffb5f1a0000 00007ffb5f29a000 C:\WINDOWS\System32\ucrtbase.dll ModLoad: 00007ffb606e0000 00007ffb60789000 C:\WINDOWS\System32\shcore.dll ModLoad: 00007ffb4e290000 00007ffb4e4f9000
C:\WINDOWS\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.17134.472_none_fb3f9af53068156d\comctl32.DLL ModLoad: 00007ffb5ca60000 00007ffb5caf8000
C:\WINDOWS\system32\uxtheme.dll ModLoad: 00007ffb608f0000 00007ffb6098e000 C:\WINDOWS\System32\msvcrt.dll ModLoad: 00007ffb601e0000 00007ffb60304000 C:\WINDOWS\System32\RPCRT4.dll ModLoad: 00007ffb60a60000 00007ffb60d82000
C:\WINDOWS\System32\combase.dll ModLoad: 00007ffb5fc40000 00007ffb5fcba000 C:\WINDOWS\System32\bcryptPrimitives.dll ModLoad: 00007ffb627a0000 00007ffb62841000 C:\WINDOWS\System32\advapi32.dll ModLoad: 00007ffb610d0000 00007ffb6112b000
C:\WINDOWS\System32\sechost.dll ModLoad: 00007ffb57b30000 00007ffb57bc6000 C:\WINDOWS\System32\TextInputFramework.dll (3d80.256c): Break instruction exception - code 80000003 (first chance) ntdll!LdrpDoDebuggerBreak+0x30: 00007ffb`6294c93c cc
int 3

InterfaceHandleshould beout, notref, but that shouldn't change much your issue. Beyond that the declaration looks fine. I can't reproduce the error 8. When I tried to open devices on my PC, I only get error 50 (ERROR_NOT_SUPPORTED). Maybe the issue is with this specific USB 'trezor' device. - Simon Mourier