65.9K
CodeProject 正在变化。 阅读更多。
Home

KB 文章 823206 的修复

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.34/5 (9投票s)

2004 年 6 月 14 日

3分钟阅读

viewsIcon

61122

downloadIcon

541

如何解决 KB 823206 中描述的行为

引言

在 Windows 2003 Server 中,Microsoft 更改了即插即用检测例程。如果系统包含多个网络接口卡,则可能出现以下症状

  • 分配给“本地连接”的 NIC 并不总是第一个 NIC。第二个 NIC 和“本地连接 2”等也是如此。
  • 调用 SetupDiEnumDeviceInfo() 返回的顺序与 NIC 的内置顺序不同。 这也包括 WMI。
  • 第一个检测到的 NIC(有时确实是第一个 NIC)的友好名称具有后缀 #2 或更高版本。

本文提供了上述问题的解决方案。 我不知道在哪里发布这个。 所以我认为 CodeProject 是最好的地方。

背景

一位客户已经向 Microsoft 报告了这种行为。 请查看 KB 文章 823206。

KB 823206 摘要

原因

无法保证检测设备的顺序,并且顺序可能会更改以优化任何平台或出于任何影响定时的其他原因。 PCI 总线上编号较高的插槽中的设备可能会在 PCI 总线上编号较低的插槽中的相同设备之前被枚举。 对于网络适配器,net 类安装程序按照设备枚举的顺序分配设备管理器中显示的友好名称

解决方法

要解决此问题,请重命名“网络连接”中列出的“本地连接”。

不错的解决方法... 不是吗?

解决方案

每个人都期望分配给“本地连接”的 NIC 是第一个 NIC。 第二个 NIC 被分配给“本地连接 2”,依此类推。 因此,我们需要做的就是

  • 枚举所有 NIC 设备
  • 获取它们的总线编号、总线上的设备编号以及设备上的功能编号
  • 使用上述三个标准对它们进行排序
  • 将网络连接名称从“本地连接”重命名为“本地连接 x”

网络连接名称存储在

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}

对于每个网络连接,相应 NIC 的 PNP 实例 ID 存储在此注册表项下。

如何枚举网络设备

SP_DEVINFO_DATA deviceInfoData;
HDEVINFO hDevInfo;

hDevInfo = SetupDiGetClassDevs(&GUID_NDIS_LAN_CLASS, NULL,
   NULL, DIGCF_DEVICEINTERFACE); 
for (i=0; SetupDiEnumDeviceInfo(hDevInfo,i,&deviceInfoData) ;i++) 
{
    //
    //
    //
}

这将枚举所有属于 GUID_NDIS_LAN_CLASS 类的设备。

接下来,我们多次调用 SetupDiGetDeviceRegistryProperty() 以获取以下属性

总线编号 总线编号表示设备连接到的 BUS 的 ID。
设备编号 这是上面指定的 BUS 上的设备编号(从 0 开始)。
功能编号 一个 NIC 可能包含多个网络端口。 因此,功能编号 0 是第一个端口,1 是第二个端口,依此类推。
PDO 名称 物理设备对象名称。 此值通过 P'n'P 管理器在检测扫描时分配。 通过按字母顺序对该值进行排序,我们可以找出设备的内置顺序。 示例字符串:\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;

通过收集的信息,我们可以使用以下方式对它们进行排序

  • iBusNumber
  • iDeviceNumber
  • iFunctionNumber

  • PDO 的名称(按字母顺序排序)

使用代码

您可以在此页面顶部下载完全可用的演示项目。

要重新编译,需要以下开发者包

  • Windows XP/2003 SDK
  • 最新的 DDK

历史

  • 2004 年 6 月 8 日 -- 1.0 版
    • 初始创建
© . All rights reserved.