Search code examples
windowswinapidriversetupapi

Can I use SetupDiEnumDeviceInterfaces to get a DevicePath from SetupDiGetDeviceInterfaceDetail when no InterfaceClassGUID is known?


Overview...

I have read How to get device interface GUID for a device? and How to open a handle to a device using its Device Instance ID?, but I'm still confused about how I am to (or whether I should even) use SetupDiEnumDeviceInterfaces paired with SetupDiGetDeviceInterfaceDetail to get a DevicePath that can be opened with CreateFile to access the device when no device interface class GUID is known. My question is based on the MSDN article here and here which rely on these functions.

More details...

The high level of my problem is I've got an audio USB device I need to send control transfer commands. To do so, I want to use WinUSB's API, and to do that I need to get a handle to the device via CreateFile. Unfortunately, there is no .inf file associated with the device and so there is no known device interface class GUID. If one plugs the device in, Windows associates with it usbaudio.sys as the driver. To start talking over WinUSB, I use libwdi to install WinUSB as the device driver so that I can communicate with it via the WinUSB API. To accomplish the install of WinUSB, libwdi dynamically creates a self-signed .cat and .inf file pair, which unfortunately has no device interface class defined. In fact, the INF file has the following in it:

[NoDeviceInterfaceGUID]
; Avoids adding a DeviceInterfaceGUID for generic driver

So, despite the fact I now have swapped out usbaudio.sys for winusb.sys, I'm unable to communicate with the device.

At first I thought maybe I should try using GUID_DEVINTERFACE_USB_DEVICE, reasoning that this is WinUSB and therefore seen as a generic USB device. But, if I invoke

SetupDiEnumDeviceInterfaces(_deviceList,
   &devInfo,
   &GUID_DEVINTERFACE_USB_DEVICE,
   0,
   &usbInterface)

it fails and GetLastError returns 259, or ERROR_NO_MORE_ITEMS immediately. I'm assuming this means it doesn't implement the interface?

So, in general, when no interface class guid is known, am I able to use the aforementioned functions in any form to get the full device path? Thanks in advance for your time.


Solution

  • Yes, first you need to get a device information set for devices that implement the interface via SetupDiGetClassDevs, then pick one of the devices and read its device interface data, and use that to get the device interface detail that contains the device path.

    Example:

    #include <windows.h>
    #include <setupapi.h>
    #include <initguid.h>
    #include <usbioctl.h>
    #include <stdio.h>
    
    BOOL DevicePath(_In_ LPCGUID interfaceGuid, DWORD instance)
    {
        BOOL result = FALSE;
        PSP_DEVICE_INTERFACE_DETAIL_DATA_W devInterfaceDetailData = NULL;
    
        HDEVINFO hDevInfo = SetupDiGetClassDevsW(interfaceGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
        if(hDevInfo == INVALID_HANDLE_VALUE)
            goto cleanup;
    
        SP_DEVICE_INTERFACE_DATA devInterfaceData;
        devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        if(!SetupDiEnumDeviceInterfaces(hDevInfo, NULL, interfaceGuid, instance, &devInterfaceData))
            goto cleanup;
    
        // Call this with an empty buffer to the required size of the structure.
        ULONG requiredSize;
        SetupDiGetDeviceInterfaceDetailW(hDevInfo, &devInterfaceData, NULL, 0, &requiredSize, NULL);
        if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
            goto cleanup;
    
        devInterfaceDetailData = malloc(requiredSize);
        if(!devInterfaceDetailData)
            goto cleanup;
    
        devInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
        if(!SetupDiGetDeviceInterfaceDetailW(hDevInfo, &devInterfaceData, devInterfaceDetailData, requiredSize, &requiredSize, NULL))
            goto cleanup;
    
        wprintf(L"%s\n", devInterfaceDetailData->DevicePath);
        result = TRUE;
    
    cleanup:
        if(hDevInfo != INVALID_HANDLE_VALUE)
            SetupDiDestroyDeviceInfoList(hDevInfo);
        free(devInterfaceDetailData);
        return result;
    }
    
    int main()
    {
        for(DWORD i = 0; DevicePath(&GUID_DEVINTERFACE_USB_DEVICE, i); ++i)
           ;
    }