question

DJm00n avatar image
0 Votes"
DJm00n asked FayWang-MSFT commented

[C++/WinRT][Win32][UWP] HidDevice.FromIdAsync() cannot open device in FileAccessMode::ReadWrite mode in Win32 app

I trying to use Windows.Devices.HumanInterfaceDevice UWP API to open HID device (gamepad in my case) and send HID output report. But HidDevice.FromIdAsync() method cannot open device in FileAccessMode::ReadWrite mode in my classic console Win32 app:

enter image description here

Here is minimal C++/WinRT repro example:

cpp
#include "pch.h"

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Devices.HumanInterfaceDevice.h>
#include <winrt/Windows.Storage.h>

#include <string>
#include <iostream>
#include <functional>

using namespace std;
using namespace winrt;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Devices::HumanInterfaceDevice;
using namespace Windows::Storage;

void OnDeviceAdded(const winrt::Windows::Devices::Enumeration::DeviceInformation& deviceInformation)
{
    std::cout << "*****Device Init End*******" << std::endl;

    try {
        string id = to_string(deviceInformation.Id());
        string name = to_string(deviceInformation.Name());

        std::cout << "New HID device connected:" << std::endl;
        std::cout << "Id: " << id << std::endl;
        std::cout << "Name: " << name << std::endl;

        HidDevice m_Device = HidDevice::FromIdAsync(deviceInformation.Id(), FileAccessMode::ReadWrite).get();

        if (!m_Device)
        {
            std::cout << "Cannot open device for writing! Trying read-only..." << std::endl;
            m_Device = HidDevice::FromIdAsync(deviceInformation.Id(), FileAccessMode::Read).get();
        }

        if (!m_Device)
        {
            std::cout << "Cannot open device for reading!" << std::endl;
            throw;
        }

        uint16_t vendorId = m_Device.VendorId();
        uint16_t productId = m_Device.ProductId();
        uint16_t version = m_Device.Version();
        uint16_t usagePage = m_Device.UsagePage();
        uint16_t usageId = m_Device.UsageId();

        std::cout << "VendorId: " << vendorId << std::endl;
        std::cout << "ProductId: " << productId << std::endl;
        std::cout << "Version: " << version << std::endl;
        std::cout << "UsagePage: " << usagePage << std::endl;
        std::cout << "UsageId: " << usageId << std::endl;
    }
    catch (...)
    {
        std::cout << "Something went wrong while device init! :(" << std::endl;
    }

    std::cout << "*****Device Init End*******" << std::endl;
}

hstring hidDeviceSelector;
DeviceWatcher hidDeviceWatcher = nullptr;
event_token hidAddedToken;

int main()
{
    winrt::init_apartment();

    hidDeviceSelector = HidDevice::GetDeviceSelector(0x01 /*HID_USAGE_PAGE_GENERIC*/, 0x05 /*HID_USAGE_GENERIC_GAMEPAD*/);
    hidDeviceWatcher = DeviceInformation::CreateWatcher(hidDeviceSelector);
    hidAddedToken = hidDeviceWatcher.Added(std::bind(&OnDeviceAdded, std::placeholders::_2));
    hidDeviceWatcher.Start();

    while (true)
    {
        // wait
    }

    hidDeviceWatcher.Added(hidAddedToken);

    winrt::uninit_apartment();

    return 0;
}


Similar questions regarding UWP apps mentioning that there should be *.appxmanifest like this:

xml
 <Capabilities>
    <DeviceCapability Name="humaninterfacedevice">
      <!--SuperMutt Device-->
      <Device Id="vidpid:045E 0610">
        <Function Type="usage:FFAA 0001" />
      </Device>
    </DeviceCapability>
  </Capabilities>

But seems this is only needed for UWP apps.

It worth to mention that HidDevice Class have DualApiPartition attribute and API should be allowed to be called from any desktop app.

This is bug or I'm missing something?

My C++/WinRT demo app code is here:
https://github.com/DJm00n/HidGamepadConsoleApp


windows-uwp
· 5
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Based on the Remark part of HidDevice.FromIdAsync, it mentions the manifest for your app must declare a specific HID device capability before you invoke this method. So maybe you can use Windows Application Packaging Project to convert your desktop app to desktop bridge app and then declare the HID device capability in its manifest. After that, run the project and see if it can work.


0 Votes 0 ·

I don't want to use MSIX package to distribute my program. Seems I found possible solution for newest Windows 10 Version 2004 (May 2020). Will give it a try.

0 Votes 0 ·

@FayWang-MSFT Interesting thing is that as I understand from remark that you linked: if there is no manifest with corresponding humaninterfacedevice capability - it shouldn't open device even for read-only. But it works. Strange.

0 Votes 0 ·

Isn't it more likely that you're not getting user consent? Based on that remark, it seems like the user will have to agree to let you open the device for write. I'm not sure the rational behind that requirement, but it is frustrating to say the least.

0 Votes 0 ·

The reason this app doesn't need to add capability is it is a windows console app, not uwp app, it doesn't has any limit. In addition, when you try the methods from this document, do you have any progress?


0 Votes 0 ·

0 Answers