Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Fix for KB article 823206

0.00/5 (No votes)
13 Jun 2004 1  
How to solve the behaviour described in KB 823206

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 this function fails, the device is 

// not a physical device (no address).

//

if (SetupDiGetDeviceRegistryProperty( hDevInfo, &deviceInfoData, 
  SPDRP_ADDRESS, &dwDataType, buff, MY_BUFFER_SIZE, NULL) == FALSE)
    continue;


// 

// the lower 16 bits are the function number, 

// the upper 16 bits are the device number

//

iFunctionNumber = ((int)buff[0]) + (((int)buff[1])<<0x10);
iDeviceNumber = ((int)buff[2]) + (((int)buff[3])<<0x10);


//

// Get the bus number (DWORD)

//

if (SetupDiGetDeviceRegistryProperty( hDevInfo, &deviceInfoData, 
  SPDRP_BUSNUMBER, &dwDataType, buff, MY_BUFFER_SIZE, NULL) == FALSE)
    continue;
iBusNumber = (int) *(DWORD*)buff;


//

// We need to know the pnp instance ID

//

if (SetupDiGetDeviceInstanceId( hDevInfo, &deviceInfoData, 
  wBuff, MY_BUFFER_SIZE, &dwSizeRequired) == FALSE)
    continue;
strPnpInstanceId =  wBuff;


//

// get the PDO name (optional).

// This name is assigned to the device from the Pnp manager.

// This shows also the order in which the devices are enumerated

// Sorting them using a string comparer will also show the built in order

//

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
    • Initial Creation

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here