Open UsbDevice using C++ WRL - ERROR_INVALID_HANDLE
Asked Answered
S

1

8

I'm trying to obtain a Windows::Devices::Usb::UsbDevice object referencing a specific USB device I have in order to pass it to a 3rd party plugin. Due to project limitations, I cannot use the C++/CX extension.

After looking at countless threads, answers and references, I came up with a initial implementation that does some black magic to invoke static methods on the WinRT classes I need. The only issue is that, even though no call results a failed HRESULT, the last call to FromIdAsync doesn't work, giving me ERROR_INVALID_HANDLE (6) as a result of GetLastError().

Simply reading the error name makes me thing the mistake is in getting the device's ID, as that's the only handle I pass on that call, but I tried passing a constant string instead (which I knew was correct) and it yielded the same results.

This is how I'm invoking FromIdAsync*:

// Retrieves static methods for UsbDevice class
ComPtr<IUsbDeviceStatics> usbDevSt;
hr = GetActivationFactory(
    HStringReference(RuntimeClass_Windows_Devices_Usb_UsbDevice).Get(),
    &usbDevSt
);

// Creates an event to work as a 'semaphore', for waiting for the 'FromIdAsync'
// call to be completed
Event openEvent(CreateEventEx(
    nullptr,
    nullptr,
    CREATE_EVENT_MANUAL_RESET,
    WRITE_OWNER | EVENT_ALL_ACCESS
));
if (!openEvent.IsValid()) return nullptr;

// Setups a callback for when the device enumeration is done
auto asyncOpenCb = Callback<IAsyncOperationCompletedHandler<UsbDevice*>>(
    [&openEvent](IAsyncOperation<UsbDevice*> *opHandler, AsyncStatus status) -> HRESULT {
        if (!opHandler || status != AsyncStatus::Completed) {
            DWORD x = GetLastError(); // ERROR_INVALID_HANDLE (6)
        }

        SetEvent(openEvent.Get());
        return S_OK;
    }
);

// Invokes the 'asyncOpenOp' method, equivalent to UsbDevice::FromIdAsync(String)
ComPtr<IAsyncOperation<UsbDevice*>> asyncOpenOp;
hr = usbDevSt->FromIdAsync(
    devId.Get(),
    asyncOpenOp.GetAddressOf()
);

// Registers completed callback
hr = asyncOpenOp->put_Completed(asyncOpenCb.Get());

// Waits for open operation to complete before continuing
WaitForSingleObjectEx(openEvent.Get(), INFINITE, false);

// Retrieves the result from the asynchronous call
ComPtr<IUsbDevice> dev;
hr = asyncOpenOp->GetResults(dev.GetAddressOf());

And this is how I'm getting devId*:

// Retrieve static methods for DeviceInformation class
ComPtr<IDeviceInformationStatics> devInfoSt;
HRESULT hr = GetActivationFactory(
    HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(),
    &devInfoSt
);

// Create an event to work as a 'semaphore', for waiting for the 'FindAllAsyncAqsFilter' call to be completed
Event findEvent(CreateEventEx(
    nullptr,
    nullptr,
    CREATE_EVENT_MANUAL_RESET,
    WRITE_OWNER | EVENT_ALL_ACCESS
));
if (!findEvent.IsValid()) return nullptr;

// Setup a callback for when the device enumeration is done
auto asyncFindCb = Callback<IAsyncOperationCompletedHandler<DeviceInformationCollection*>>(
    [&findEvent](IAsyncOperation<DeviceInformationCollection*> *opHandler, AsyncStatus status) -> HRESULT {
        SetEvent(findEvent.Get());
        return S_OK;
    }
);

// Invoke the 'FindAllAsyncAqsFilter' method, equivalent to DeviceInformation::FindAllAsync(String)
ComPtr<IAsyncOperation<DeviceInformationCollection*>> asyncFindOp;
hr = devInfoSt->FindAllAsyncAqsFilter(
    HStringReference(DEVICE_FILTER).Get(),
    asyncFindOp.GetAddressOf()
);

// Registers completed callback
hr = asyncFindOp->put_Completed(asyncFindCb.Get());

// Waits for enumeration to complete before continuing
WaitForSingleObjectEx(findEvent.Get(), INFINITE, FALSE);

// Retrieves the result from the asynchronous call
ComPtr<IVectorView<DeviceInformation*>> devColl;
hr = asyncFindOp->GetResults(devColl.GetAddressOf());

// Checks for collection size
unsigned int collSize;
hr = devColl->get_Size(&collSize);

if (collSize == 0) {
    return nullptr;
}

// Retrieves the first DeviceInformation object from the collection
ComPtr<IDeviceInformation> devInfo;
hr = devColl->GetAt(0, devInfo.GetAddressOf());

// Retrieves the device's id
HString devId;
hr = devInfo->get_Id(devId.GetAddressOf());

Also, I do initialize WinRT, this way:

RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
if (FAILED(initialize)) return nullptr;

*Multiple if (FAILED(hr)) return nullptr; removed for brevity.

Subcontract answered 20/4, 2016 at 13:8 Comment(2)
You cannot get a diagnostic from GetLastError(), you must use the HRESULT.Backhand
@HansPassant I thought that could be the case. But which one in this case? Because all of them return S_OK except for the last one (asyncOpenOp->getResults(...)), which gives me 0x8000000E (A method was called at an unexpected time), which makes completely sense because the operation has already failed.Subcontract
B
0

GetLastError function

Retrieves the calling thread's last-error code value.
The last-error code is maintained on a per-thread basis.
Multiple threads do not overwrite each other's last-error code.


but I tried passing a constant string instead (which I knew was correct) and it yielded the same results.

ERROR_INVALID_HANDLE
The caller passed NULL in the InterfaceHandle parameter.

As mentioned above you must get the result from the HRESULT

[&openEvent](IAsyncOperation<UsbDevice*> *opHandler, AsyncStatus status) -> HRESULT 

I would try to loop over the collection to figure out how to get the error from

https://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.enumeration.deviceinformationcollection.indexof

Batish answered 28/4, 2016 at 12:11 Comment(1)
The collection doesn't seem to be the problem. I can successfully retrieve the DeviceInformation object and its properties seem very valid. I didn't really understand your suggestion.Subcontract

© 2022 - 2024 — McMap. All rights reserved.