2
votes

When going to Windows' "Device manager" and clicking on (almost) any device in the list one piece of information in the "General" tab is called "Location". That is a string that is either:

  • human-readable, like "on NVIDIA GeForce GTX 1080"
  • semi-useful, like "Location 0 (Internal High Definition Audio Bus)" or "PCI bus 9, device 0, function 0"
  • a USB location, like "Port_#0004.Hub_#0015" or even a "0009.0000.0000.004.000.000.000.000.000"

That info is available through Windows' Unified device property model APIs.

What I'm looking for is to get that information from a given IMFActivate object.

Is there a way to do so? I can't find how to get "device" info from that activation object. The only piece of data I have is it's "symbolic link" (in my case, this string: \\?\usb#vid_04b4&pid_8888&mi_00#9&4fe28be&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\global) but that link's format is nothing like the string I see in "Location".

Thus my question: how to get device's "Location" string, given its IMFActivate object?

UPDATE

Here's the code I'm using to "convert" the symbolic link, provided by IMFActivate to a device id string, recognizable by setup-api functions and then extracting the "location string":

CString symLink2Location(const CString & _symLink)
{
    DEVINST di;
    CString devId = _symLink;
    devId = devId.Left( devId.Find(L"#{") );
    devId.Replace(L"\\\\?\\", L"");
    devId.Replace(L"#", L"\\");
    const auto rc = CM_Locate_DevNodeW(&di, devId.GetBuffer(), CM_LOCATE_DEVNODE_NORMAL);
    if(rc == CR_SUCCESS){
        DEVPROPTYPE dpt;
        ULONG sz = MAX_PATH;
        WCHAR prop[MAX_PATH];
        if(CM_Get_DevNode_PropertyW(di, &DEVPKEY_Device_LocationInfo, &dpt, (PBYTE)&prop, &sz, 0) == CR_SUCCESS){
            if(dpt == DEVPROP_TYPE_STRING){
                return prop;
            }
        }
    }
    return L"";
}

UPDATE 2

Here are the 3 audio input devices as seen in devmgmt.msc under "Sound, video and game controllers":

  • MS LifeCam Cinema (TM), location: 0000.0014.0000.013.003.000.000.000.000 (symlink: \\?\SWD#MMDEVAPI#{0.0.1.00000000}.{751fe058-cef2-4d28-bbeb-e438981938d7}#{2eef81be-33fa-4800-9670-1cd474972c3f})
  • MS LifeCam Studio (TM), location: 0000.0014.0000.013.004.004.000.000.000 (symlink: \\?\SWD#MMDEVAPI#{0.0.1.00000000}.{59267d2e-940b-45f5-8655-45372787bd85}#{2eef81be-33fa-4800-9670-1cd474972c3f})
  • SUB2r USB 3.0 HD Webcam, location: 0009.0000.0000.004.000.000.000.000.000 (symlink: \\?\SWD#MMDEVAPI#{0.0.1.00000000}.{26a4f608-cbd8-4206-b958-d57ee6847153}#{2eef81be-33fa-4800-9670-1cd474972c3f})

All 3 are USB devices, all 3 are listed when calling MFEnumDeviceSources but their "symbolic link" doesn't resolve into a hardware device.

1
if you have symbolic link( sz) - call CM_Locate_DevNodeW(&dnDevInst, sz, CM_LOCATE_DEVNODE_NORMAL) and then CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_LocationInfo, &PropertyType, pb, &rcb, 0) and you got locationRbMm
The call to CM_Locate_DevNodeW() returns CR_INVALID_DEVICE_ID. I suppose the "symbolic link" provided by the IMFActivation object is not what the setup-api expects :( (see my original post for a sample of what that link looks like)YePhIcK
yes, my mistake. really need device id here, which in your case will be usb\vid_04b4&pid_8888&mi_00\9&4fe28be&0&0000 exactlyRbMm
i know internal (undocumented) rules how symbolic link ins generated from device id (append \\?\, convert \ to #, append class guid in your case this is {e5323777-f976-4f5b-9b55-b94699c46e44} (STATIC_KSCATEGORY_VIDEO_CAMERA) but how do this in documented way not sureRbMm
found correct way - need first call CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId with string returned by IMFAttributes::Get[Allocated]String and then already use returned device id in call CM_Locate_DevNodeW + CM_Get_DevNode_PropertyWRbMm

1 Answers

2
votes

the string returned from IMFAttributes::Get[Allocated]String with MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK or MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_SYMBOLIC_LINK this is device interface string which we can use as input to CM_Get_Device_Interface_PropertyW. for get location information (if it present) need do 3 steps:

  1. call CM_Get_Device_Interface_PropertyW with DEVPKEY_Device_InstanceId - as result we got device instance identifier of a device
  2. use returned string in call CM_Locate_DevNodeW
  3. and finally call CM_Get_DevNode_PropertyW with DEVPKEY_Device_LocationInfo

code example:

CONFIGRET PrintLocation(PCWSTR pszDeviceInterface)
{
    ULONG cb = 0, rcb = 64;

    static volatile UCHAR guz;

    PVOID stack = alloca(guz);
    DEVPROPTYPE PropertyType;

    CONFIGRET err;

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

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

        if (!(err = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0)))
        {
            if (PropertyType == DEVPROP_TYPE_STRING)
            {
                DbgPrint("InstanceId=%S\n", sz);

                DEVINST dnDevInst;

                if (!(err = CM_Locate_DevNodeW(&dnDevInst, sz, CM_LOCATE_DEVNODE_NORMAL)))
                {
                    do 
                    {
                        if (cb < rcb)
                        {
                            rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
                        }

                        if (!(err = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_LocationInfo, &PropertyType, pb, &rcb, 0)))
                        {
                            if (PropertyType == DEVPROP_TYPE_STRING)
                            {
                                DbgPrint("Location=%S\n", sz);
                            }
                            else
                            {
                                err = CR_WRONG_TYPE;
                            }
                        }

                    } while (err == CR_BUFFER_SMALL);
                }
            }
            else
            {
                err = CR_WRONG_TYPE;
            }

            break;
        }

    } while (err == CR_BUFFER_SMALL);

    return err;
}

of course if hardcode buffer size, function can make more simply

CONFIGRET PrintLocationSimp(PCWSTR pszDeviceInterface)
{
    WCHAR buf[1024];

    DEVPROPTYPE PropertyType;

    ULONG BufferSize = sizeof(buf);

    CONFIGRET err;

    if (!(err = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, (PBYTE)buf, &BufferSize, 0)))
    {
        if (PropertyType == DEVPROP_TYPE_STRING)
        {
            DbgPrint("InstanceId=%S\n", buf);

            DEVINST dnDevInst;

            if (!(err = CM_Locate_DevNodeW(&dnDevInst, buf, CM_LOCATE_DEVNODE_NORMAL)))
            {
                BufferSize = sizeof(buf);

                if (!(err = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_LocationInfo, &PropertyType, (PBYTE)buf, &BufferSize, 0)))
                {
                    if (PropertyType == DEVPROP_TYPE_STRING)
                    {
                        DbgPrint("Location=%S\n", buf);
                    }
                    else
                    {
                        err = CR_WRONG_TYPE;
                    }
                }
            }
        }
        else
        {
            err = CR_WRONG_TYPE;
        }
    }

    return err;
}

and for IMFActivate we can use next code:

void mftest()
{
    IMFAttributes *pAttributes;

    if (SUCCEEDED(MFCreateAttributes(&pAttributes, 1)))
    {
        UINT32 count, cchLength;
        IMFActivate **ppDevices, *pDevice;

        if (SUCCEEDED(pAttributes->SetGUID(
            MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)) && 
            SUCCEEDED(MFEnumDeviceSources(pAttributes, &ppDevices, &count)) &&
            count)
        {
            PVOID pv = ppDevices;

            do 
            {
                pDevice = *ppDevices++;

                PWSTR pszDeviceInterface;

                if (SUCCEEDED(pDevice->GetAllocatedString(
                    MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &pszDeviceInterface, &cchLength)))
                {
                    DbgPrint("%S\n", pszDeviceInterface);

                    PrintLocation(pszDeviceInterface);

                    CoTaskMemFree(pszDeviceInterface);
                }

            } while (--count);

            CoTaskMemFree(pv);
        }

        pAttributes->Release();
    }
}