给定一个 device instance ID对于网卡,我想知道它的MAC地址。我的系统上用于集成英特尔千兆位卡的示例设备实例 ID:
PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
到目前为止,我使用的算法如下:
SetupDiGetClassDevs
与 DIGCF_DEVICEINTERFACE
. SetupDiEnumDeviceInfo
在 SP_DEVINFO_DATA
中获取返回的设备. SetupDiEnumDeviceInterfaces
与 GUID_NDIS_LAN_CLASS
获取设备接口(interface)。 SetupDiGetDeviceInterfaceDetail
对于这个返回的设备接口(interface)。这为我们提供了字符串形式的设备路径:\\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}
CreateFile
打开它使用#4 的结果。 DeviceIoControl
与 IOCTL_NDIS_QUERY_GLOBAL_STATS
和 OID_802_3_PERMANENT_ADDRESS
的 OID获取MAC地址。 这通常有效,并且已在相当多的机器上成功使用。但是,似乎极少数机器的网络驱动程序无法正确响应
DeviceIoControl
在步骤#6 中请求;即使将网卡驱动程序更新到最新版本,问题仍然存在。这些是较新的基于 Windows 7 的计算机。具体来说,DeviceIoControl
成功完成,但返回零字节而不是包含 MAC 地址的预期六个字节。IOCTL_NDIS_QUERY_GLOBAL_STATS
的 MSDN 页面上似乎有一条线索。 :This IOCTL will be deprecated in later operating system releases. You should use WMI interfaces to query miniport driver information. For more information see, NDIS Support for WMI.
-- 也许较新的网卡驱动程序不再实现此 IOCTL?
那么,我应该如何让这个工作?我的方法是否有可能存在疏忽而我做的事情有些不对劲?还是我需要采取更不同的方法?一些替代方法似乎包括:
Win32_NetworkAdapter
WMI 类:提供所需的信息,但由于性能不佳而被拒绝。见 Fast replacement for Win32_NetworkAdapter WMI class for getting MAC address of local computer MSNdis_EthernetPermanentAddress
WMI 类:似乎是 IOCTL_NDIS_QUERY_GLOBAL_STATS
的 WMI 替代品并直接从驱动程序查询 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......
)。 GetAdaptersAddresses
或 GetAdaptersInfo
(已弃用)。我能在返回值中找到的唯一非本地化标识符是适配器名称,它是一个类似 {28FD5409-15BD-4C06-B62F-004D3A06F852}
的字符串。 - 与 DeviceName
相同由 WMI NDIS 类返回。同样,我无法弄清楚如何将它与设备实例 ID 相关联。我不确定它是否会在 100% 的时间内工作 - 例如用于未配置 TCP/IP 协议(protocol)的适配器。 似乎如果我能找到一种方法来从设备实例 ID 中获取卡的“GUID”,那么我就可以用剩下的两种方法中的一种来做事了。但我还没想好怎么做。否则,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 返回非零表示成功,但随后返回零字节。
最佳答案
这是一种方法:
GetAdaptersAddresses
获取 IP_ADAPTER_ADDRESSES
的列表结构 AdapterName
获取其 GUID字段(我不确定这种行为是否得到保证,但我系统中的所有适配器都在这里有一个 GUID,并且文档说 AdapterName
是永久性的)HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\<the adapter GUID>\Connection\PnPInstanceID
读取注册表项(如果存在)(从 here 得到这个想法;在 Google 上搜索该 key 似乎有据可查,因此不太可能更改)PCI\VEN_14E4&DEV_16B1&SUBSYS_96B11849&REV_10\4&2B8260C3&0&00E4
)IP_ADAPTER_ADDRESSES
并查看 PhysicalAddress
领域如果没有一百万种方法来做某事,它就不会是 Windows!
关于c++ - 如何在给定设备实例 ID 的情况下快速可靠地获取网卡的 MAC 地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12608757/