Introduction
With Windows 2003 Server, Microsoft changed the Plug'n'Play detection routine. If a system contains multiple network interface cards, the following symptoms can occur:
- The NIC assigned to "Local Area Connection" is not always the first NIC. This is the same for the second NIC and "Local Area Connection 2" and so on.
- The order returned by calling
SetupDiEnumDeviceInfo()
is not the same as the build in order of the NICs. This includes WMI also.
- The friendly name of the first detected NIC (which is sometimes really the first NIC) has the postfix #2 or higher.
This article provides a solution for the problem described above. I didn't know where to publish this. So I think CodeProject is the best place.
Background
A customer reported this behaviour to Microsoft already. Check out KB article 823206.
Abstract of KB 823206:
Cause:
The order in which devices are detected is not guaranteed, and the order can change to optimize any platform or for any other
reason that affects the timing. A device in a higher-numbered slot on a PCI bus can be enumerated prior to an identical device
in a lower-numbered slot on the PCI bus. In the case of network adapters, the net class installer assigns the friendly name that is
shown in Device Manager in the order that the devices were enumerated
Workaround:
To work around this problem, rename the Local Area Connections that are listed in Network Connections.
|
Nice workaround... isn't it?
Solution
Everyone expects that the NIC assigned to "Local Area Connection" is the first NIC. The second NIC is assigned to "Local Area Connection 2"
and so on. Because this, the only thing we need to do is:
- Enum all NIC devices
- Get their bus number, device number on the bus, and function number on the device
- Sort them using the three criteria above
- Rename the network connection name starting from "Local Area Connection" to "Local Area Connection x"
The network connection names are stored under:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}
|
To each network connection, the PNP instance ID of the corresponding NIC is stored under this registry key.
How to enumerate the network devices:
SP_DEVINFO_DATA deviceInfoData;
HDEVINFO hDevInfo;
hDevInfo = SetupDiGetClassDevs(&GUID_NDIS_LAN_CLASS, NULL,
NULL, DIGCF_DEVICEINTERFACE);
for (i=0; SetupDiEnumDeviceInfo(hDevInfo,i,&deviceInfoData) ;i++)
{
}
This will enumerate all devices which are belong to the GUID_NDIS_LAN_CLASS
class.
Next we call SetupDiGetDeviceRegistryProperty()
a few times to get out the following properties:
Bus Number |
The bus number represents the ID of the BUS, where the device is connected to. |
Device Number |
This is the number of the device on the BUS specified above (starting a 0). |
Function Number |
A NIC may contain have multiple network ports. So function number 0 is the first port, 1 the second and so on. |
PDO Name |
Physical Device Object Name. This value assigned through the P'n'P manager at the detection scan. By sorting this value alphabetically, we can find out the built in order of the devices. Sample string: \Device\NTPNP_PCI0014 |
#define MY_BUFFER_SIZE 4096
int iFunctionNumber;
int iDeviceNumber;
int iBusNumber;
CString strPdoName;
CString strPnpInstanceId;
BYTE buff[MY_BUFFER_SIZE+1];
WCHAR wBuff[MY_BUFFER_SIZE+1];
DWORD dwDataType = 0;
.
.
.
if (SetupDiGetDeviceRegistryProperty( hDevInfo, &deviceInfoData,
SPDRP_ADDRESS, &dwDataType, buff, MY_BUFFER_SIZE, NULL) == FALSE)
continue;
iFunctionNumber = ((int)buff[0]) + (((int)buff[1])<<0x10);
iDeviceNumber = ((int)buff[2]) + (((int)buff[3])<<0x10);
if (SetupDiGetDeviceRegistryProperty( hDevInfo, &deviceInfoData,
SPDRP_BUSNUMBER, &dwDataType, buff, MY_BUFFER_SIZE, NULL) == FALSE)
continue;
iBusNumber = (int) *(DWORD*)buff;
if (SetupDiGetDeviceInstanceId( hDevInfo, &deviceInfoData,
wBuff, MY_BUFFER_SIZE, &dwSizeRequired) == FALSE)
continue;
strPnpInstanceId = wBuff;
if (SetupDiGetDeviceRegistryProperty( hDevInfo, &deviceInfoData,
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, &dwDataType,
buff, MY_BUFFER_SIZE, NULL) == FALSE)
continue;
strPdoName = (LPWSTR) buff;
With the collected information, we can sort them using:
iBusNumber
iDeviceNumber
iFunctionNumber
or
- The name of the PDO (alphabetical sort)
Using the code
You can download the fully working demo project at the top of this page.
To recompile, the following developer packs are required:
- Windows XP/2003 SDK
- The newest DDK
History
- June, 08. 2004 -- Version 1.0