sidit77 / async-hid

A async Rust library for interacting with HID devices
MIT License
4 stars 2 forks source link

winrt: open device ReadWrite fails #7

Open zardini123 opened 6 months ago

zardini123 commented 6 months ago

When trying the example read_write on some devices I own, every time the process does not exit successfully with the error:

Error: HidError: Custom("Failed to open \\\\?\\HID#VID_203A&PID_FFFC&MI_02#7&34ad9e6b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}")
        at C:\...\async-hid\src\backend\winrt\mod.rs:124:28

I tried opening with ReadWrite on my mouse, keyboard, PS4, and PS5 controllers, and all failed. I have integrated async-hid into a personal project and async-hid has an identical problem there.

The only AccessMode that works is Read. I can successfully acquire read reports with all my devices listed.

Opening devices for read/write on Windows does not happen when using hidapi.

Any help would be greatly appreciated. Thanks!

sidit77 commented 6 months ago

Some of these are unfortunatly not supported:

The Windows.Devices.HumanInterfaceDevice API supports most HID devices. However, it blocks the top-level application collection represented by the following usage pages, to prevent conflict with other Windows APIs and OS behavior:

HID_USAGE_PAGE_UNDEFINED HID_USAGE_PAGE_GENERIC HID_USAGE_GENERIC_KEYBOARD HID_USAGE_GENERIC_KEYPAD HID_USAGE_GENERIC_SYSTEM_CTL HID_USAGE_PAGE_KEYBOARD HID_USAGE_PAGE_CONSUMER HID_USAGE_PAGE_DIGITIZER HID_USAGE_PAGE_SENSOR HID_USAGE_PAGE_BARCODE_SCANNER HID_USAGE_PAGE_WEIGHING_DEVICE HID_USAGE_PAGE_MAGNETIC_STRIPE_READER HID_USAGE_PAGE_TELEPHONY

This is the relevent section of hidapi:

let handle = open_device(&device_path, true)
        // System devices, such as keyboards and mice, cannot be opened in
        // read-write mode, because the system takes exclusive control over
        // them.  This is to prevent keyloggers.  However, feature reports
        // can still be sent and received.  Retry opening the device, but
        // without read/write access.
        .or_else(|_| open_device(&device_path, false))?;

I don't think it's possible to replicate with winrt as FileAccessMode can only be Read or ReadWrite

zardini123 commented 6 months ago

Thanks for your response! I have some follow-up questions.

Some of these are unfortunatly not supported:

This still begs the question why my gamepads (PS4, PS5) aren't opening. Is there something I am missing when calling your API?

This is the relevent section of hidapi:

Where can I find the source of this section? I am having troubles finding it.

I don't think it's possible to replicate with winrt as FileAccessMode can only be Read or ReadWrite

What would be the ultimate solution be then for these devices? Does hidapi use a different windows API that is different to acquire access?

sidit77 commented 6 months ago

Just for clarification: Are you trying to open the gamepad in read or read/write mode?

Where can I find the source of this section? I am having troubles finding it.

Original C code: https://github.com/libusb/hidapi/blob/d0732cda906ad07b7e1ef93f1919035643620435/windows/hid.c#L991 My Rust port: https://github.com/ruabmbua/hidapi-rs/blob/2f37587cc7d7d56f2cdf3c948abffac860d63b5c/src/windows_native/mod.rs#L398 (hidapi could use either depending on if you activated the windows_native feature)

Does hidapi use a different windows API that is different to acquire access?

Yes. What hidapi essentially uses the win32 way of opening devices, while this library uses the higher level winrt classes (virtually no unsafe, automatic async, at lot less code). The reason the backend is called winrt instead of windows is because I thought that it might become nessecary to add a second Windows backend (win32).

zardini123 commented 6 months ago

Just for clarification: Are you trying to open the gamepad in read or read/write mode?

Read/write mode :)

(hidapi could use either depending on if you activated the windows_native feature)

Thanks for the links! From my brief scan of the windows-native section of async-hid, it seems the implementation is similar to the winrt implementation of this library, but synchronized. So is the windows_native part of hidapi using winrt, and the C implementation is using win32?

Yes. What hidapi essentially uses the win32 way of opening devices, while this library uses the higher level winrt classes (virtually no unsafe, automatic async, at lot less code). The reason the backend is called winrt instead of windows is because I thought that it might become nessecary to add a second Windows backend (win32).

Oh, that is really interesting! I appreciate the forward-thinking on the backend organization.

My thought is until the win32 backend is implemented, I think it would be useful to notify users why those devices are not opening is because it is inherit to the winrt backend. Would it be possible to check if the HID_USAGE for the device attempting to be opened is any of those usage types you mentioned, and then throw a more crafted error for this scenario?

sidit77 commented 6 months ago

So is the windows_native part of hidapi using winrt, and the C implementation is using win32?

No both of them are using win32. The windows-native section is more or less a one-to-one port of the C implementation.

A lot of these restrictions are not really inherent to winrt but Windows as a whole. Other operating systems have other restrictions. On Linux for example you can't open any device unless you're either running your program as root or installed the appropriate udev rules. On MacOS you'll get a permission popup for the user, that can be denied and probably more. With WASM/WebHID (not yet in main) you'll have to manually ask for a similar permission popup before you can even enumerate devices and enumerate will only find devices that the user granted access to.