0

A word in before: Much of text and code follows, but I try to explain my problem (which occurs randomly) as much detailed as possible:

My application, which is dealing with the network interfaces via thet Setup API, fails randomly, most likely when trying to use SetupDiGetDeviceRegistryProperty(). The main problem I have is that the list of devices returned by SetupDiEnumDeviceInfo() (I use this to get the Index of a specific network adapter which will be passed to SetupDiGetDeviceRegistryProperty()) alters in an "unexpected" way. My first problem was that devices appear and/or disappear from this list. As stated here, the first thing I found out that using

const GUID *netGUID = &GUID_DEVCLASS_NET;    
SetupDiGetClassDevs(netGUID, 0, 0, DIGCF_PROFILE)

instead of

const GUID *netGUID = &GUID_DEVCLASS_NET;
SetupDiGetClassDevs(netGUID, 0, 0, DIGCF_PRESENT)

always returns all the network adapters that can be seen in device manager (including the "hidden" = "not installed" ones). This worked so far as expected - with Windows 10 (64 bit).

After being glad that it worked, I compiled the application for use with Windows 7 32 bit and tested it. Unfortunately I have to see that there is still a problem - SetupDiEnumDeviceInfo() still returns all the network adapters as expected, but the order is not the same at all and (a) subsequent call(s) if SetupDiGetDeviceRegistryProperty() fail with ERROR_INVALID_DATA. Most of the time I get

WAN Miniport (IP), WAN Miniport (IPv6), WAN Miniport (PPPOE), Microsoft ISATAP Adapter, Microsoft ISATAP Adapter #2, WAN Miniport (PPTP), Microsoft ISATAP Adapter #3, WAN Miniport (SSTP), Intel(R) I210 Gigabit Network Connection, Intel(R) Ethernet Connection I219-LM, RAS Async Adapter, WAN Miniport (IKEv2), WAN Miniport (L2TP), WAN Miniport (Network Monitor)

but sometimes the order changes this way:

Intel(R) I210 Gigabit Network Connection,Intel(R) Ethernet Connection I219-LM,Microsoft ISATAP Adapter,Microsoft ISATAP Adapter #2,Microsoft ISATAP Adapter #3,WAN Miniport (IKEv2),WAN Miniport (L2TP),WAN Miniport (Network Monitor),WAN Miniport (IP),WAN Miniport (IPv6),WAN Miniport (PPPOE),WAN Miniport (PPTP),WAN Miniport (SSTP),RAS Async Adapter

Using the (Qt) code below may cause that the index I determined does not match the intended device any more. Is there a way to prevent that the order of the device's list alters, or is there something I'm doing wrong?

// Get a list of all network adpters known by the system
const QStringList adapterNames()
{
    HDEVINFO hIfDevs;
    quint32 index = 0;
    SP_DEVINFO_DATA infoData;
    const GUID *netGUID = &GUID_DEVCLASS_NET;
    QStringList adapterNames = QStringList();
    CHAR friendlyName[MAX_DEVICE_NAME];

    hIfDevs = SetupDiGetClassDevs(netGUID, 0, 0, DIGCF_PRESENT);

    if (hIfDevs == INVALID_HANDLE_VALUE)
    {
        qWarning() << "Getting device class info failed with error " << GetLastError();
        return QStringList();
    }

    SecureZeroMemory(&infoData, sizeof(SP_DEVINFO_DATA));
    infoData.cbSize = sizeof(SP_DEVINFO_DATA);

    while (SetupDiEnumDeviceInfo(hIfDevs, index, &infoData))
    {
        SecureZeroMemory(friendlyName, sizeof(friendlyName));

        if (!SetupDiGetDeviceRegistryPropertyA(hIfDevs, &infoData, SPDRP_FRIENDLYNAME, NULL, (LPBYTE)friendlyName, MAX_DEVICE_NAME, NULL))
        {
            if (!SetupDiGetDeviceRegistryPropertyA(hIfDevs, &infoData, SPDRP_DEVICEDESC, NULL, (LPBYTE)friendlyName, MAX_DEVICE_NAME, NULL))
            {
                qWarning() << "SetupDiGetDeviceRegistryPropertyA() for #" << index << "returned error " << GetLastError();
            }
        }

        if(friendlyName[0])
        {
            adapterNames.append(QString::fromLocal8Bit(friendlyName));
        }

        index++;
    }

SetupDiDestroyDeviceInfoList(hIfDevs);
return adapterNames;
}

Is used by:

// Get PCI bus/slot/device/funcntion number (as 0xBBSSDDFF)
bool getPCIHWaddr(quint32 deviceIndex, ...)
{
    HDEVINFO hIfDevs;
    SP_DEVINFO_DATA infoData;    
    const GUID *netGUID = &GUID_DEVCLASS_NET;

    if (hIfDevs == INVALID_HANDLE_VALUE)
    {
        qCCritical(lcOS) << "getPCIHWaddr: Getting device class info failed with error " << GetLastError();
        return 0;
    }

    hIfDevs = SetupDiGetClassDevs(netGUID, 0, 0, DIGCF_PRESENT);

    if (hIfDevs == INVALID_HANDLE_VALUE)
    {
        qCritical() << "getPCIHWaddr: Getting device class info failed with error " << GetLastError();
        return 0;
    }

    if (!SetupDiEnumDeviceInfo(hIfDevs, deviceIndex, &infoData))
    {
        qCritical() << "getPCIHWaddr: Enumerating device information failed with error " << GetLastError();
        return 0;
    }

    // --> In failing scenario, this call returns ERROR_INVALID_DATA (13)
    if (!SetupDiGetDeviceRegistryPropertyA(hIfDevs, &infoData, SPDRP_BUSNUMBER, NULL, (LPBYTE)&devProp, sizeof(quint32), NULL))
    {
        qCritical() << "getPCIHWaddr: Query PCI bus number failed with error " << GetLastError();
        return 0;
    }

    ...
}

Is used by:

// Get informations on adapter by interface ID found in Registry (HKLM\SYSTEM\CurrentControlSet\Services\TcpIp\Parameter\Interfaces)
bool netIfInfo(const QString &id,...)
{
    QStringList adapterNames = adapterNames();

    /*
     * ...
     * Lot of stuff enumerating data from GetIfTable(), GetAdaptersAddresses(), until the expected deivce was found (stored QString deviceDesc)
     * ...
     */

    // --> Here it happens...
    if (!getPCIHWaddr(adapterNames.indexOf(deviceDesc), &PCIAddr, &PnPId))
    {
        ...
    }

    ...
}
Willy K.
  • 397
  • 2
  • 14
  • why you try get registry property instead property ? and i think more easy and more efficient use *CM_* api instead *SetupDi* api. – RbMm Mar 11 '19 at 17:08
  • @RbMm: Thanks for your input. The code above was written a long time before and, besides the fact that porting all that code will not make things easier, I am not familiar with the "CM api". Back to the roots: How can I a avoid that the device list changes between SetupDi*() calls? – Willy K. Mar 12 '19 at 09:12

1 Answers1

0

I think the problem here is that getPCIHWAddr() uses the index of a device info set that is already destroyed. I added a function that returns pointers to HDEVINFO and SP_DEVINFO_DATA, but does not call SetupDiDestroyDeviceInfoList(), this is done by getPCIHWAddr() after it has done it's job - and see: That works (Win7 and Win10).

Willy K.
  • 397
  • 2
  • 14