6

I have two USB device IDs, e.g. USB\VID_E4F1&PID_0661\00000115FA9CE7750000000000000000 and USB\VID_E4F1&PID_0661&MI_00\7&B5A5DDF&0&0000

How to verify device #2 is a direct child of device #1 (physically they’re different parts of the same USB composite device)?

In real-life scenarios it will be many of them connected to the same USB controller. Moreover, it's possible they'll be of the same maker and model. That's why I cant verify the VID, PID, and use Win32_USBControllerDevice WMI query to verify they're plugged into the same USB controller - I need to somehow verify the parent-child relation, not just the fact they're plugged into the same controller.

If it matters, I only need to support Windows 8+.

Soonts
  • 20,079
  • 9
  • 57
  • 130
  • 1
    I'm no expert, but I think CM_Locate_DevNode, CM_Get_Parent, and CM_Get_Device_ID should do the trick. – Harry Johnston Sep 24 '14 at 04:55
  • @HarryJohnston you've forgot CM_Get_Device_ID_Size, but thanks a lot, it works like a charm! You should probably copy-paste your comment to the answer. – Soonts Sep 24 '14 at 15:10
  • You tagged the question C#, but you accepted an answer that uses WINAPI... Shouldn't the c# tag be removed? – gog Jul 03 '19 at 13:37
  • @gog As far as I remember, the project I was working at that time was 100% C#. It can consume WinAPI just fine, see e.g. https://www.pinvoke.net/default.aspx/setupapi/CM_Locate_DevNodeA%20.html – Soonts Jul 03 '19 at 14:01

3 Answers3

6

The PnP Configuration Manager API is your friend here:

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
5

If you are able to use the new WinRT API You should take a look at the PnpObject class and namespace.

Here's a code example:

var propertiesToQuery = new List<string>() { 
    "System.ItemNameDisplay",
    "System.Devices.DeviceInstanceId",
    "System.Devices.Parent",
    "System.Devices.LocationPaths",
    "System.Devices.Children"
};

var id1 = @"USB\VID_E4F1&PID_0661\00000115FA9CE7750000000000000000";

var device1 = await PnpObject.FindAllAsync(PnpObjectType.Device, 
                                            propertiesToQuery, 
                                            "System.Devices.DeviceInstanceId:=\"" + id1 + "\"");

var id2 = @"USB\VID_E4F1&PID_0661&MI_00\7&B5A5DDF&0&0000";

var device2 = await PnpObject.FindAllAsync(PnpObjectType.Device, 
                                            propertiesToQuery, 
                                            "System.Devices.DeviceInstanceId:=\"" + id2 + "\"");


var parent1 = device1.Properties["System.Devices.Parent"] as string;
var parent2 = device2.Properties["System.Devices.Parent"] as string;

if (parent1 && parent1 == id2)
{
    WriteLine("Device 2 is parent of device 1");
}

if (parent2 && parent2 == id1)
{
    WriteLine("Device 11 is parent of device 2");
}

var child_ids = device1.Properties["System.Devices.Children"] as string[];

if (child_ids != null){
    foreach (var id in child_ids)
    {
        if (id == id2){
            WriteLine("Device 2 is child of device 1")
        }
    }
}

 child_ids = device2.Properties["System.Devices.Children"] as string[];

if (child_ids != null){
    foreach (var id in child_ids)
    {
        if (id == id1){
            WriteLine("Device 1 is child of device 2")
        }
    }
}

If this is not enough you can try going up or down the parent/children path.

You could also look at the System.Devices.LocationPaths property (which is an array of strings) and test if one is a prefix of the other.

Sirotnikov
  • 444
  • 4
  • 10
-1

Extending the great answer by Harry Johnston:

Once you call CM_Locate_DevNode, you can get the parent Device Instance ID with a single function call:

#include <propkey.h>

// Get the Parent Device Property
DEVPROPTYPE propType;
wchar_t propBuf[MAX_DEVICE_ID_LEN] = {};
ULONG propBufSize = sizeof(propBuf);
CONFIGRET cres = CM_Get_DevNode_Property(devInst, (DEVPROPKEY*)&PKEY_Devices_Parent, &propType, (BYTE*)propBuf, &propBufSize, 0);
if (cres == CR_SUCCESS) {
    // propBuf now contains the Parent System Device ID!
}
gog
  • 1,220
  • 11
  • 30