Jam*_*ton 9 c++ windows wmi networking device-instance-id
给定网卡的设备实例ID,我想知道它的MAC地址.集成Intel千兆卡的系统上的设备实例ID示例:
PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
Run Code Online (Sandbox Code Playgroud)
到目前为止,我使用的算法的工作原理如下:
SetupDiGetClassDevs给DIGCF_DEVICEINTERFACE.SetupDiEnumDeviceInfo以获取返回的设备SP_DEVINFO_DATA.SetupDiEnumDeviceInterfaces与GUID_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和OID 调用OID_802_3_PERMANENT_ADDRESS以获取MAC地址.这通常有效,并已在相当多的机器上成功使用.但是,似乎很少有机器的网络驱动程序DeviceIoControl在步骤#6 中没有对请求做出正确响应; 即使将网卡驱动程序更新到最新版本后问题仍然存在.这些是较新的基于Windows 7的计算机.具体来说,DeviceIoControl成功完成,但返回零字节而不是包含MAC地址的预期六个字节.
一条线索似乎在MSDN页面上IOCTL_NDIS_QUERY_GLOBAL_STATS:
此IOCTL将在以后的操作系统版本中弃用.您应该使用WMI接口来查询微型端口驱动程序信息.有关更多信息,请参阅NDIS对WMI的支持.
- 也许更新的网卡驱动程序不再实现这个IOCTL?
那么,我应该怎么做呢?是否有可能在我的方法中存在疏忽并且我做了一些稍微错误的事情?或者我需要采取更加不同的方法吗?一些替代方法似乎包括:
Win32_NetworkAdapterWMI类:提供所需信息但由于性能糟糕而被拒绝.请参阅Win32_NetworkAdapter WMI类的快速替换以获取本地计算机的MAC地址MSNdis_EthernetPermanentAddressWMI类:似乎是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......).GetAdaptersAddresses或GetAdaptersInfo(已弃用).我可以在返回值中找到的唯一非本地化标识符是适配器名称,它是一个字符串,与WMI NDIS类返回的字符串{28FD5409-15BD-4C06-B62F-004D3A06F852}相同DeviceName.所以,我再也无法弄清楚如何将它与设备实例ID相关联.我不确定它是否会在100%的时间内工作 - 例如,对于没有配置TCP/IP协议的适配器.看起来如果我能找到一种方法从设备实例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();
}
Run Code Online (Sandbox Code Playgroud)
代码失败,打印:
GetMACAddress: invalid address length of 0.
Run Code Online (Sandbox Code Playgroud)
因此,DeviceIoControl返回非零指示成功,但随后返回零字节.
我最终使用SetupDiGetDeviceRegistryProperty阅读SPDRP_FRIENDLYNAME。如果没有找到,那么我SPDRP_DEVICEDESC就会阅读。最终,这给我带来了一个类似“VirtualBox Host-Only Ethernet Adapter #2”的字符串。然后,我将其与 WMI NDIS 类(WMI 类)中的 InstanceName 属性进行匹配MSNdis_EthernetPermanentAddress。如果有多个适配器共享同一驱动程序(即“#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 类进行匹配。