1

I am converting a project to a UWP App, and thus have been following guidelines outlined in the MSDN post here. The existing project heavily relies on CreateFile() to communicate with connected devices.

There are many posts in SO that show us how to get a CreateFile()-accepted device path using SetupAPI's SetupDiGetDeviceInterfaceDetail() Is there an alternative way to do this using the PnP Configuration Manager API? Or an alternative, user-mode way at all?

I had some hope when I saw this example in Windows Driver Samples github, but quickly became dismayed when I saw that the function they used in the sample is ironically not intended for developer use, as noted in this MSDN page.

AMemberofDollars
  • 331
  • 2
  • 14
  • 1
    `not intended for developer use` this is not true. in what your problem ? for local machine you can use functions without *_Ex* suffix – RbMm May 26 '17 at 21:19
  • 1
    `CM_Get_Device_Interface_List_Ex` with 0 in last param *HMACHINE hMachine* have the same effect as [`CM_Get_Device_Interface_List`](https://msdn.microsoft.com/en-us/library/windows/hardware/ff538463(v=vs.85).aspx) - so simply use this api – RbMm May 26 '17 at 21:23
  • In the github sample, they use CM_Get_Device_Interface_List_Ex to somehow get a device path; does the non-Ex function have the same capability? – AMemberofDollars May 26 '17 at 22:59
  • from [this](https://msdn.microsoft.com/en-us/library/windows/hardware/ff538463(v=vs.85).aspx) is the "device instance id" the same as the device path? Maybe this is the source of my confusion. – AMemberofDollars May 26 '17 at 23:02
  • 1
    `CM_Get_Device_Interface_List` call `CM_Get_Device_Interface_List_Ex` with `HMACHINE hMachine = 0` so for local computer yes - it absolute the same. [*Device Instance ID*](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/device-instance-ids) this is not a path which can be used in `CreateFile` call. this is absolute different thing. – RbMm May 26 '17 at 23:53
  • 1
    and about code in your example - returning string is *multiple, NULL-terminated Unicode strings, each representing the symbolic link name of an interface instance.* so here can be more than 1 path. and can be empty path - also. so in general you need in loop enum path – RbMm May 27 '17 at 00:06
  • 1
    something like this `while (*sz) { /*CreateFile(sz,..)*/ sz += 1 + wcslen(sz);}` – RbMm May 27 '17 at 00:07
  • I'm not sure what you are showing me here then; I'm trying to get the device path. I need find a method that gives me the device path that does not involve setupapi. I'm not sure how what you are talking about can help with me get the device path; which I need to use with CreateFile(). – AMemberofDollars May 27 '17 at 00:59
  • 1
    `sz` - this i mean string in place `devicePath` in example. this example show you how give device path and this worked. in what problem ? not compiled, error executed ? – RbMm May 27 '17 at 01:07
  • How do I get the device path; I'm not asking how do I use it. – AMemberofDollars May 27 '17 at 01:33
  • Also please speak using the eloquent English you have used in other answers on your history page. I'm sure it won't be a problem as all of the previous comments are all intentional; unless they are not? – AMemberofDollars May 27 '17 at 01:43

2 Answers2

0

function GetDevicePath in general correct and can be used as is. about difference between CM_*(..) and CM_*_Ex(.., HMACHINE hMachine) - the CM_*(..) simply call CM_*_Ex(.., NULL) - so for local computer versions with and without _Ex suffix the same.

about concrete GetDevicePath code - call CM_Get_Device_Interface_List_Size and than CM_Get_Device_Interface_List only once not 100% correct - because between this two calls new device with this interface can be arrived to system and buffer size returned by CM_Get_Device_Interface_List_Size can be already not enough for CM_Get_Device_Interface_List. of course possibility of this very low, and you can ignore this. but i prefer make code maximum theoretical correct and call this in loop, until we not receive error other than CR_BUFFER_SMALL. also need understand that CM_Get_Device_Interface_List return multiple, NULL-terminated Unicode strings - so we need iterate here. in [example] always used only first returned symbolic link name of an interface instance. but it can be more than 1 or at all - 0 (empty). so better name function - GetDevicePaths - note s at the end. i be use code like this:

ULONG GetDevicePaths(LPGUID InterfaceClassGuid, PWSTR* pbuf)
{
    CONFIGRET err;

    ULONG len = 1024;//first try with some reasonable buffer size, without call *_List_SizeW

    for(PWSTR buf;;) 
    {
        if (!(buf = (PWSTR)LocalAlloc(0, len * sizeof(WCHAR))))
        {
            return ERROR_NO_SYSTEM_RESOURCES;
        }

        switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
        {
        case CR_BUFFER_SMALL:
            err = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        default:
            LocalFree(buf);
            if (err)
            {
                return CM_MapCrToWin32Err(err, ERROR_UNIDENTIFIED_ERROR);
            }
            continue;

        case CR_SUCCESS:
            *pbuf = buf;
            return NOERROR;
        }
    }
}

and usage example:

void example()
{
    PWSTR buf, sz;
    if (NOERROR == GetDevicePaths((GUID*)&GUID_DEVINTERFACE_VOLUME, &buf))
    {
        sz = buf;
        while (*sz)
        {
            DbgPrint("%S\n", sz);

            HANDLE hFile = CreateFile(sz, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

            if (hFile != INVALID_HANDLE_VALUE)
            {
                // do something
                CloseHandle(hFile);
            }

            sz += 1 + wcslen(sz);
        }

        LocalFree(buf);
    }
}

so we must not simply use in returned DevicePathS (sz) only first string, but iterate it

    while (*sz)
    {
      // use sz
        sz += 1 + wcslen(sz);
    }
RbMm
  • 31,280
  • 3
  • 35
  • 56
0

I got a valid Device Path to a USB Hub Device, and used it successfully to get various device descriptors by sending some IOCTLs, by using the function I posted in my own answer to another question

I'm reporting the same function below:

This function returns a list of NULL-terminated Device Paths (that's what we get from CM_Get_Device_Interface_List())

You need to pass it the DEVINST, and the wanted interface GUID.

Since both the DEVINST and interface GUID are specified, it is highly likely that CM_Get_Device_Interface_List() will return a single Device Path for that interface, but technically you should be prepared to get more than one result.

It is responsibility of the caller to delete[] the returned list if the function returns successfully (return code 0)

int GetDevInstInterfaces(DEVINST dev, LPGUID interfaceGUID, wchar_t**outIfaces, ULONG* outIfacesLen)
{
    CONFIGRET cres;
    if (!outIfaces)
        return -1;
    if (!outIfacesLen)
        return -2;

    // Get System Device ID
    WCHAR sysDeviceID[256];
    cres = CM_Get_Device_ID(dev, sysDeviceID, sizeof(sysDeviceID) / sizeof(sysDeviceID[0]), 0);
    if (cres != CR_SUCCESS)
        return -11;

    // Get list size
    ULONG ifaceListSize = 0;
    cres = CM_Get_Device_Interface_List_Size(&ifaceListSize, interfaceGUID, sysDeviceID, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    if (cres != CR_SUCCESS)
        return -12;

    // Allocate memory for the list
    wchar_t* ifaceList = new wchar_t[ifaceListSize];

    // Populate the list
    cres = CM_Get_Device_Interface_List(interfaceGUID, sysDeviceID, ifaceList, ifaceListSize, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    if (cres != CR_SUCCESS) {
        delete[] ifaceList;
        return -13;
    }

    // Return list
    *outIfaces = ifaceList;
    *outIfacesLen = ifaceListSize;

    return 0;
}

Please note that, as RbMm already said in his answer, you may get a CR_BUFFER_SMALL error from the last CM_Get_Device_Interface_List() call, since the device list may have been changed in the time between the CM_Get_Device_Interface_List_Size() and CM_Get_Device_Interface_List() calls.

gog
  • 1,220
  • 11
  • 30