c++ - 如何在给定设备实例 ID 的情况下快速可靠地获取网卡的 MAC 地址

标签 c++ windows networking wmi device-instance-id

给定一个 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}
  • 此时我们就有了一个指向网卡驱动程序接口(interface)的地址。用 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)的适配器。
  • NetBIOS 方法:需要在卡上设置特定协议(protocol),因此无法 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 返回非零表示成功,但随后返回零字节。

    最佳答案

    这是一种方法:

  • 调用 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 似乎有据可查,因此不太可能更改)
  • 从此 key 中您可以获得适配器的设备 ID(类似于: 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/

    相关文章:

    windows - 在 WSL(Linux 的 Windows 子系统)上挂载磁盘镜像?

    python - 如何在所有操作系统中使用 pygame 播放声音?

    networking - qemu 中 -net user 和 -net nic 的区别

    node.js - 网络应用程序和服务器应用程序之间的区别

    c++ - 获取对 XIChangeProperty 的 undefined reference

    c++ - 如何使用 QTimeLine 为两个值设置动画

    c++ - *.h文件和*.cpp文件中派生类的定义和声明

    c++ - 在实例方法中使用 find_if

    windows - 如何在 Windows 主机上启动/引导期间启动 Vagrant?

    Java简单网络程序在Eclipse中运行,而不是在终端中运行