给定网卡的设备实例 ID,我想知道它的 MAC 地址。我的系统上用于集成英特尔千兆位卡的示例设备实例 ID:
PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
到目前为止,我使用的算法如下:
SetupDiGetClassDevs
用调用DIGCF_DEVICEINTERFACE
。- 调用
SetupDiEnumDeviceInfo
以获取返回的设备SP_DEVINFO_DATA
。 - 调用
SetupDiEnumDeviceInterfaces
withGUID_NDIS_LAN_CLASS
以获取设备接口。 - 调用
SetupDiGetDeviceInterfaceDetail
此返回的设备接口。这将设备路径作为字符串获取:\\?\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
OIDOID_802_3_PERMANENT_ADDRESS
以获取 MAC 地址。
这通常有效,并且已在相当多的机器上成功使用。但是,似乎极少数机器的网络驱动程序无法正确响应DeviceIoControl
步骤#6 中的请求;即使将网卡驱动程序更新到最新版本,问题仍然存在。这些是较新的基于 Windows 7 的计算机。具体来说,DeviceIoControl
成功完成,但返回零字节而不是包含 MAC 地址的预期六个字节。
MSDN页面上似乎有一条线索IOCTL_NDIS_QUERY_GLOBAL_STATS
:
此 IOCTL 将在以后的操作系统版本中弃用。您应该使用 WMI 接口来查询微型端口驱动程序信息。有关详细信息,请参阅 NDIS 对 WMI 的支持。
——也许更新的网卡驱动不再实现这个 IOCTL?
那么,我应该如何让这个工作?我的方法是否可能存在疏忽,我做错了什么?还是我需要采取更不同的方法?一些替代方法似乎包括:
- 查询
Win32_NetworkAdapter
WMI 类:提供所需信息,但由于性能糟糕而被拒绝。请参阅快速替换 Win32_NetworkAdapter WMI 类以获取本地计算机的 MAC 地址 - 查询
MSNdis_EthernetPermanentAddress
WMI 类:似乎是IOCTL_NDIS_QUERY_GLOBAL_STATS
直接从驱动程序中查询 OID 的 WMI 替代品 - 这个适用于麻烦的网络驱动程序。不幸的是,返回的类实例只提供 MAC 地址和InstanceName
,这是一个本地化字符串,如Intel(R) 82567LM-2 Gigabit Network Connection
. 查询MSNdis_EnumerateAdapter
产生一个与 a 相关的列表InstanceName
,DeviceName
比如\DEVICE\{28FD5409-15BD-4C06-B62F-004D3A06F852}
。我不确定如何从DeviceName
即插即用设备实例 ID (PCI\VEN_8086......
) 转到。 - 调用
GetAdaptersAddresses
或GetAdaptersInfo
(已弃用)。我可以在返回值中找到的唯一非本地化标识符是适配器名称,它是一个类似于 - 的字符串,与 WMI NDIS 类返回的{28FD5409-15BD-4C06-B62F-004D3A06F852}
相同。DeviceName
再说一次,我不知道如何将它与设备实例 ID 关联起来。我不确定它是否会在 100% 的时间内工作——例如,对于没有配置 TCP/IP 协议的适配器。 - NetBIOS 方法:需要在卡上设置特定协议,因此无法 100% 工作。通常看起来像hack-ish,而不是我所知道的与设备实例ID相关的方法。我会拒绝这种方法。
- UUID 生成方法:拒绝,原因我这里不再详述。
似乎如果我能找到一种方法从设备实例 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 返回非零表示成功,但随后返回零字节。