How to open a handle to a device using its Device Instance ID?

2.5k views Asked by At

I am updating my question to better reflect what I was actually going after. To state a fact about my original confusion quickly, it is incorrect to say that there is a 1-to-1 relationship between "Device Interface Class GUID" and the Device Instance ID. A device can have many device interfaces. As Ben Voigt noted in the comments, see this for more information.


How can one open a handle to a child device after calling the CM_Get_Child (...) function?

Take the following code snip as an example:

#pragma comment (lib, "Setupapi.lib")
#pragma comment (lib, "Cfgmgr32.lib")

#include <iostream>
#include <Windows.h>
#include <Setupapi.h>
#include <Cfgmgr32.h> 

#define GUID_STRING_SIZE 40

int main ()
{
    CONFIGRET CMResult = CR_SUCCESS;
    WCHAR DeviceInstanceID[] = L"USB\\VID_2109&PID_0813\\8&216C1825&0&4\0"; // Parent Device Instance ID.

    DEVNODE ParentDeviceNode = (DWORD) 0; // A device instance handle. This handle is bounded to the local machine.
    CMResult = CM_Locate_DevNode ((PDEVINST) &ParentDeviceNode, DeviceInstanceID, CM_LOCATE_DEVNODE_NORMAL);

    if (CMResult != CR_SUCCESS)
    {
        std::cout << "No parent device node found." << std::endl;
        return -1;
    }
    else
    {
        DEVINST NextChildDeviceNode = (DWORD) 0;
        CMResult = CM_Get_Child ((PDEVINST) &NextChildDeviceNode, ParentDeviceNode, 0x0);    // Gets the first child of the parent node. If this returns "CR_NO_SUCH_DEVNODE," then there is no child attached.

        if (CMResult != CR_SUCCESS)
        {
            std::cout << "No child device node found." << std::endl;
            return -2;
        }
        else
        {
            ULONG ChildInstanceIDBuffLength = 0;
            CMResult = CM_Get_Device_ID_Size (&ChildInstanceIDBuffLength, NextChildDeviceNode, 0x0);

            if (CMResult != CR_SUCCESS)
            {
                std::cout << "Could not get the size of the device instance ID of child device." << std::endl;
                return -3;
            }
            else
            {
                WCHAR * ChildInstanceIDBuff = (WCHAR *) malloc (ChildInstanceIDBuffLength);
                CMResult = CM_Get_Device_IDW (NextChildDeviceNode, ChildInstanceIDBuff, ChildInstanceIDBuffLength, 0x0);

                if (CMResult != CR_SUCCESS)
                {
                    std::cout << "Could not actual device instance ID string of child device" << std::endl;
                    return -4;
                }
                else
                {
                    std::cout << "Found child device instance ID: ";
                    std::wcout << ChildInstanceIDBuff << std::endl;

                    /*
                     *  Open handle to the child device node now!
                     */
                }

                free (ChildInstanceIDBuff);
            }
        }
    }

    return 0;
}

How can I use the newly obtained child Device Instance ID to open a handle to the device? CreateFile (...) requires the complete device path, which includes the missing "Device Interface Class GUID."

More specifically, a device path has the following format:
\\?\usb#vid_2109&pid_0813#7&3981C8D6&0&2#{[DEVICE_INTERFACE_GUID]}, where:

  1. [DEVICE_INTERFACE_GUID] - This the "Device Interface Class GUID." This is NOT the same as the "Device Setup Class GUID."

There does not appear to be an easy way to get this "Device Interface Class GUID" without some level of brute force (e.g. CM_Enumerate_Classes (...) using the CM_ENUMERATE_CLASSES_INTERFACE flag). Is there a function I can call to get a handle to a device using only its "Device Instance ID," so that I can then call DeviceIoControl (...) and query information about the device?

2

There are 2 answers

6
Ben Voigt On

You can use the CM_Enumerate_Classes function with the CM_ENUMERATE_CLASSES_INTERFACE flag (Requires Windows 8) to get possible values to pass as that third parameter of SetupDiEnumDeviceInterfaces.

Beginning with Windows 8 and later operating systems, callers can use the ulFlags member to specify which device classes CM_Enumerate_Classes should return. Prior to Windows 8, CM_Enumerate_Classes returned only device setup classes.

Note that finding all interfaces on a device is very useful for diagnosing driver problems and/or reverse engineering random peripherals rescued from scrap. But you should know what interface class you are dealing with long before you get to the point of calling CreateFile.

18
RbMm On

if we need open handle to device by Device Instance ID -

  • first call CM_Locate_DevNodeW function for obtains a device instance handle to the device node that is associated with a specified device instance ID on the local machine.
  • then we need call CM_Get_DevNode_PropertyW function with DEVPKEY_Device_PDOName - this return name of the physical name object (PDO) that represents a device and we can use it in call NtOpenFile. of course if very want - can use and in call CreateFileW if add L"\\\\?\\Global\\GLOBALROOT" to name, but not view any sense do this.

volatile UCHAR guz = 0;

ULONG OpenDeviceByDeviceID(_Out_ PHANDLE FileHandle, _In_ PWSTR DeviceID)
{
    DEVINST dnDevInst;

    CONFIGRET CmReturnCode = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);

    if (CmReturnCode == CR_SUCCESS)
    {
        ULONG cb = 0, rcb = 128;

        PVOID stack = alloca(guz);

        DEVPROPTYPE PropertyType;

        union {
            PVOID pv;
            PWSTR sz;
            PBYTE pb;
        };

        do 
        {
            if (cb < rcb)
            {
                rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
            }

            CmReturnCode = CM_Get_DevNode_PropertyW(dnDevInst, 
                &DEVPKEY_Device_PDOName, &PropertyType, pb, &rcb, 0);

            if (CmReturnCode == CR_SUCCESS)
            {
                if (PropertyType == DEVPROP_TYPE_STRING)
                {
                    DbgPrint("PDOName = %S\n", sz);

#if 1
                    IO_STATUS_BLOCK iosb;
                    UNICODE_STRING ObjectName;
                    OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
                    RtlInitUnicodeString(&ObjectName, sz);
                    NTSTATUS status = NtOpenFile(FileHandle, 
                        FILE_GENERIC_READ, &oa, &iosb, 0, 0);

                    return 0 > status ? RtlNtStatusToDosError(status) : NOERROR;
#else
                    static WCHAR prefix[] = L"\\\\?\\Global\\GLOBALROOT";
                    alloca(sizeof(prefix) - sizeof(WCHAR));

                    PWSTR fileName = sz - _countof(prefix) + 1;
                    memcpy(fileName, prefix, sizeof(prefix) - sizeof(WCHAR));

                    HANDLE hFile = CreateFileW(fileName, FILE_GENERIC_READ, 
                        0, 0, OPEN_EXISTING, 0, 0);
                    if (hFile == INVALID_HANDLE_VALUE)
                    {
                        return GetLastError();
                    }

                    *FileHandle = hFile;
                    return NOERROR;
#endif
                }
                else
                {
                    CmReturnCode = CR_WRONG_TYPE;
                }
            }

        } while (CmReturnCode == CR_BUFFER_SMALL);
    }

    return CM_MapCrToWin32Err(CmReturnCode, 0);
}