如何快速,可靠地获取网卡的MAC地址给其设备实例ID(How to reliably and qui

2019-07-31 23:50发布

给定一个设备实例ID为网卡,我想知道它的MAC地址。 我对集成Intel千兆网卡系统实例设备实例ID:

PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8

到目前为止,我已经使用的算法如下所示:

  1. 呼叫SetupDiGetClassDevsDIGCF_DEVICEINTERFACE
  2. 呼叫SetupDiEnumDeviceInfo得到所退回的设备在SP_DEVINFO_DATA
  3. 呼叫SetupDiEnumDeviceInterfacesGUID_NDIS_LAN_CLASS得到一个设备接口。
  4. 呼叫SetupDiGetDeviceInterfaceDetail此返回设备接口。 这会让我们作为一个字符串的设备路径: \\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}
  5. 在这一点上,我们有一个地址的网卡驱动程序的界面。 打开它CreateFile利用#4的结果。
  6. 打电话DeviceIoControlIOCTL_NDIS_QUERY_GLOBAL_STATS和OID OID_802_3_PERMANENT_ADDRESS得到的MAC地址。

这通常工作,并已在相当大数量的机器成功应用。 然而,似乎很少数的机器有没有正确响应网络驱动器DeviceIoControl步骤#6请求; 甚至可以更新网卡驱动程序到最新后问题仍然存在。 这些都是新的,基于Windows 7的计算机。 具体而言, DeviceIoControl成功完成,但返回零个字节而不是包含的MAC地址的预期六个字节。

一条线索似乎是在MSDN页面上IOCTL_NDIS_QUERY_GLOBAL_STATS

该IOCTL将在以后的操作系统版本中被弃用。 您应该使用WMI接口来查询微端口驱动程序的信息。 欲了解更多信息,请参阅WMI的NDIS支持。

- 也许是较新的网卡驱动程序不再执行这一IOCTL?

所以,我应该怎么得到这个工作? 是否有可能有一个在我的方法的监督和我做得稍有不妥? 或者我需要采取更多的不同的方法? 一些替代的方法似乎包括:

  • 查询Win32_NetworkAdapter WMI类:提供所需的信息,但被拒绝,由于可怕的性能。 见为Win32_NetworkAdapter WMI类快速更换为获取本地计算机的MAC地址
  • 查询MSNdis_EthernetPermanentAddress WMI类:似乎是WMI替代IOCTL_NDIS_QUERY_GLOBAL_STATS并从驱动程序直接查询OID -和麻烦的网络驱动程序这一个工程。 不幸的是,返回的类的实例只提供的MAC地址和InstanceName ,这是像一个本地化字符串Intel(R) 82567LM-2 Gigabit Network Connection 。 查询MSNdis_EnumerateAdapter产生其涉及的列表InstanceName到一个DeviceName ,像\DEVICE\{28FD5409-15BD-4C06-B62F-004D3A06F852} 我不知道如何从去DeviceName的插件和播放设备实例ID( PCI\VEN_8086...... )。
  • 呼叫GetAdaptersAddressesGetAdaptersInfo (不推荐)。 唯一的非本地化的标识符,我可以在返回值找到的是适配器名称,这就好比一个字符串{28FD5409-15BD-4C06-B62F-004D3A06F852} -一样的DeviceName由WMI NDIS类返回。 所以,再一次,我无法弄清楚如何将它涉及到设备实例ID。 我不知道它是否会工作的任一时间的100% - 例如对于没有配置TCP / IP协议适配器。
  • NetBIOS的方法:需要特定的协议进行设卡上,以便将无法正常工作时间的100%。 一般来说似乎砍十岁上下,而不是一种方法,涉及到设备实例ID反正,我知道的。 我反对这种做法。
  • UUID生成方法:拒绝的原因,我就不细说这里。

好像如果我能找到一个办法让“GUID”从设备实例ID卡,我会好我与做事的其余两种方法的一种方式。 但我还没有想出如何呢。 否则,WMI NDIS做法似乎最有前途的。

获得的网卡和MAC地址的列表是容易的,也有做的几种方法。 在,让我将它与设备实例ID的快捷方式这样做显然是硬...

编辑:IOCTL调用示例代码,如果它可以帮助任何人(忽略泄漏HFILE手柄):

HANDLE hFile = CreateFile(dosDevice.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
    DWORD err = GetLastError();
    wcout << "GetMACAddress: CreateFile on " << dosDevice << " failed." << endl;
    return MACAddress();
}
BYTE address[6];
DWORD oid = OID_802_3_PERMANENT_ADDRESS, returned = 0;
//this fails too: DWORD oid = OID_802_3_CURRENT_ADDRESS, returned = 0;
if (!DeviceIoControl(hFile, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid), address, 6, &returned, NULL)) {
    DWORD err = GetLastError();
    wcout << "GetMACAddress: DeviceIoControl on " << dosDevice << " failed." << endl;
    return MACAddress();
}
if (returned != 6) {
    wcout << "GetMACAddress: invalid address length of " << returned << "." << endl;
    return MACAddress();
}

该代码失败,打印:

GetMACAddress: invalid address length of 0.

所以DeviceIoControl的返回非零表示成功,但随后返回零个字节。

Answer 1:

下面是做这件事:

  1. 呼叫GetAdaptersAddresses得到的名单IP_ADAPTER_ADDRESSES结构
  2. 遍历每个适配器,并从一开始它的GUID AdapterName场(我不知道,如果这种行为是有保证的,但在我的系统中的所有适配器有一个GUID这里,该文档称, AdapterName是永久性的)
  3. 对于每个适配器读取从注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\<the adapter GUID>\Connection\PnPInstanceID (如果存在)(从有这个想法在这里 ;搜索在谷歌是关键似乎是有据可查的,所以它不可能改变)
  4. 从这个键你得到的设备ID的适配器(类似: PCI\VEN_14E4&DEV_16B1&SUBSYS_96B11849&REV_10\4&2B8260C3&0&00E4
  5. 直到找到一个匹配这样做对每个适配器。 当您在比赛刚回到IP_ADAPTER_ADDRESSES ,并期待在PhysicalAddress
  6. 获得啤酒(可选)

它不会是的Windows,如果有没有百万的方式做一些事情!



Answer 2:

我结束了使用SetupDiGetDeviceRegistryProperty阅读SPDRP_FRIENDLYNAME 。 如果不是发现了,然后我读SPDRP_DEVICEDESC代替。 最终,这让我像“VirtualBox虚拟主机只有以太网适配器#2”的字符串。 我再搭配这对在WMI NDIS类InstanceName属性( MSNdis_EthernetPermanentAddress WMI类)。 这两个属性时,必须注意阅读有多个适配器共享相同的驱动程序(即“2#”,“#3”等) -如果只有一个适配器然后SPDRP_FRIENDLYNAME不可用,但如果有一个以上的然后SPDRP_FRIENDLYNAME需要来区分它们。

该方法让我有点紧张,因为我比较什么似乎像一个本地化的字符串,而且也没有,我已经发现,可以保证我在做什么总是工作文档。 不幸的是,我还没有发现被记录在案的工作,无论是任何更好的方法。

一对夫妇的其他替代方法涉及无证注册表位置卑躬屈膝。 一种方法是spencercw的方法,而另一个将是读SPDRP_DRIVER ,这是下子项的名称HKLM\SYSTEM\CurrentControlSet\Control\Class 。 下面的驱动程序项,查找Linkage\Export值则似乎是它可以匹配的DeviceName中的财产MSNdis_EnumerateAdapter类。 但是,没有任何文件我能找到,说这些数值可以合法匹配。 此外,我发现他们唯一的文档Linkage\Export是从Win2000的注册表参考,并明确表示,应用程序不应该依赖于它。

另一种方法是看我原来的问题,第4步:“ SetupDiGetDeviceInterfaceDetail此返回设备接口”。 设备接口路径实际上可以被用来重建的设备路径。 开始与设备接口路径: \\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852} 。 然后,最后的斜杠之前删除一切,让你有: {28fd5409-15bd-4c06-b62f-004d3a06f852} 最后,在前面加上\Device\这个字符串匹配它的WMI NDIS类。 再次,然而,这似乎是无证依靠设备接口路径的实现细节。

最后,我调查了其他方法了,听起来至少匹配严重自己无证并发症SPDRP_FRIENDLYNAME / SPDRP_DEVICEDESC字符串。 所以,我选择了更简单的方法,这只是对阵的WMI类NDIS那些字符串。



Answer 3:

我猜你想,以实现某种DRM,库存,或分类制度的,因为你试图让永久 MAC地址,而不是当前一个拿到的MAC地址。

您似乎忘记了,甚至还有一个行政超级强加的MAC地址 (即:“强制” MAC地址)。
一些司机让您从设备属性页面做到这一点,在高级选项卡下(例如:我的Marvell网卡让我做到这一点),而另一些人不会让你这样做(读:他们不支持该属性)。

然而,它在一个注册表值的所有端部: HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress ,具有REG_SZ类型。 在这里可以设置不同的MAC地址比原来的,其形式为“01020304abcd”(6个字节,纯十六进制,而无需:分离器或0x前缀)。 当您设置它,重新启动机器,和上电的新的MAC地址才会有效果。

我恰好有一个主板具有两个集成的Marvell网卡和NETGEAR的USB无线网卡。 Marvell的一个支持MAC地址发生变化:如果你设置NetworkAddress注册表中的值,你看到在驱动程序属性页的新价值,也和它立即有效果,而不需要重新启动(如果从设备更改属性页)。 这里如下的读取与不同方法的MAC地址的结果:

  • GetAdaptersInfo :新的MAC地址
  • IOCTL_NDIS_QUERY_GLOBAL_STATS :原始MAC地址
  • MSNdis_EthernetPermanentAddress :原始MAC地址

我试图加入NetworkAddress在注册表中的NETGEAR USB无线网卡值,其结果是:

  • GetAdaptersInfo :新的MAC地址
  • IOCTL_NDIS_QUERY_GLOBAL_STATS :新的MAC地址
  • MSNdis_EthernetPermanentAddress :新的MAC地址

原来的MAC ADDRES不见了。

因此,为了不被“恶意”用户上当,你总是需要检查HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress注册表值。 如果设置,我想这是最好不要相信网络适配器可言 ,因为它是由驱动程序的实现来决定究竟会采用不同的方法来呈现给大家。

一些背景要实现这个注册表项:

有关HKLM \系统\ CurrentControlSet \类关键Microsoft文档
根据该网页上的微软文档,

没有为每个正在使用安装程序类的GUID命名的类的一个子项

所以我们选择了{4D36E972-E325-11CE-BFC1-08002BE10318}子键(又名GUID_DEVCLASS_NET ,在定义<devguid.h>进一步证明这里 )

此外,根据微软的文档,

每个类子项包含称为软件键(或,驱动键),用于安装在系统类的每个设备实例的其它子项。 这些软件键中的每一个通过使用一个设备实例ID,这是一个基本10命名,四位数序号值的XXXX部分是一个正整数的一个4个字符的文本表示形式,从0开始

所以,你可以遍历子项高达0000,0001,0002,多达网络适配器在您的系统的数量。
文档止于此:我没有找到有关不同的注册表值,或如任何其他文档。

然而,在所有这些子项,你可以找到REG_SZ值,可以帮助你链接GetAdaptersInfo() MSNdis_EthernetPermanentAddressWin32_NetworkAdapter和设备实例ID世界(这个回答你的问题)。

注册表值是:

  • DeviceInstanceID :它的值是,毫不奇怪,设备实例ID
  • NetCfgInstanceId :其值是AdapterName所述的构件IP_ADAPTER_INFO结构,通过返回GetAdaptersInfo() 。 这也是GUID的成员Win32_NetworkAdapter WMI类。
  • 不要忘了NetworkAddress一个:应该一个有效的MAC地址存在这里,驾驶员它作为MAC地址使用的报告GetAdaptersInfo() MSNdis_EthernetPermanentAddressIOCTL_NDIS_QUERY_GLOBAL_STATS

然后,你已经说了,之间的唯一连接MSNdis_EthernetPermanentAddress WMI类和“世界”的其余部分是由它的InstanceName成员。 你可以将它涉及到Description的成员IP_ADAPTER_INFO结构,由归国GetAdaptersInfo() 。 虽然它可能是一个本地化的名称,这似乎是对系统(对于我的两个集成的Marvell网卡,第二个有一个“#2”追加到它的名字)是唯一的。

最后要注意的:

说,所有上述情况, 用户可以选择禁用WMI ...



文章来源: How to reliably and quickly get the MAC address of a network card given its device instance ID