gpt4 book ai didi

c# - 快速替换Win32_NetworkAdapter WMI类以获取本地计算机的MAC地址

转载 作者:可可西里 更新时间:2023-11-01 13:54:58 25 4
gpt4 key购买 nike

这个问题的TL; DR版本:WMI Win32_NetworkAdapter类包含我需要的信息,但是太慢了。在Windows上获取MACAddress,ConfigManagerErrorCode和PNPDeviceID列的信息的更快方法是什么?

我需要检索连接的网络适配器的信息,以便可以获取MAC地址来唯一标识本地Microsoft Windows计算机。 WMI Win32_NetworkAdapter类似乎具有我正在寻找的信息。 MACAddress,ConfigManagerErrorCode和PNPDeviceID列是我真正需要的唯一列:

  • MACAddress:MAC地址(此操作的目标)
  • ConfigManagerErrorCode:允许我确定适配器是否已启用并正在运行。 (如果已禁用,则应使用我的应用先前缓存的MAC地址(如果有))。
  • PNPDeviceID:通过检查“PCI”(如果需要,可能还有其他接口(interface))的前缀,我可以过滤掉非物理适配器,在我的Windows 7盒中有几个(包括虚拟适配器,例如VMware/VirtualBox) )。

  • 我的计划是使用PNPDeviceID过滤掉非物理设备。然后,我将在剩余的所有表条目上使用MACAddress列(将地址保存到缓存中)。当设备被禁用(可能由非零的ConfigManagerErrorCode指示)并且MACAddress为空时,我可以从缓存中对该设备使用以前看到的MACAddress。

    您可以在Windows 7计算机上看到此表的内容。您可以看到其中有大量垃圾,但是只有一个带有“PCI” PNPDeviceID的条目。
    wmic:root\cli>NIC GET Caption, ConfigManagerErrorCode, MACAddress, PNPDeviceID
    Caption ConfigManagerErrorCode MACAddress PNPDeviceID
    [00000000] WAN Miniport (SSTP) 0 ROOT\MS_SSTPMINIPORT\0000
    [00000001] WAN Miniport (IKEv2) 0 ROOT\MS_AGILEVPNMINIPORT\0000
    [00000002] WAN Miniport (L2TP) 0 ROOT\MS_L2TPMINIPORT\0000
    [00000003] WAN Miniport (PPTP) 0 ROOT\MS_PPTPMINIPORT\0000
    [00000004] WAN Miniport (PPPOE) 0 ROOT\MS_PPPOEMINIPORT\0000
    [00000005] WAN Miniport (IPv6) 0 ROOT\MS_NDISWANIPV6\0000
    [00000006] WAN Miniport (Network Monitor) 0 ROOT\MS_NDISWANBH\0000
    [00000007] Intel(R) 82567LM-2 Gigabit Network Connection 0 00:1C:C0:B0:C4:89 PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
    [00000008] WAN Miniport (IP) 0 ROOT\MS_NDISWANIP\0000
    [00000009] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0000
    [00000010] RAS Async Adapter 0 20:41:53:59:4E:FF SW\{EEAB7790-C514-11D1-B42B-00805FC1270E}\ASYNCMAC
    [00000011] Microsoft Teredo Tunneling Adapter 0 ROOT\*TEREDO\0000
    [00000012] VirtualBox Bridged Networking Driver Miniport 0 00:1C:C0:B0:C4:89 ROOT\SUN_VBOXNETFLTMP\0000
    [00000013] VirtualBox Host-Only Ethernet Adapter 0 08:00:27:00:C4:A1 ROOT\NET\0000
    [00000014] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0001
    [00000015] VMware Virtual Ethernet Adapter for VMnet1 0 00:50:56:C0:00:01 ROOT\VMWARE\0000
    [00000016] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0002
    [00000017] VMware Virtual Ethernet Adapter for VMnet8 0 00:50:56:C0:00:08 ROOT\VMWARE\0001
    [00000018] Microsoft ISATAP Adapter 0 ROOT\*ISATAP\0003

    (如果禁用物理适配器,则MACAddress列将变为null,并且ConfigManagerErrorCode更改为非零)。

    不幸的是,这个类太慢了。在相对较新的基于Windows 7 Core i7的计算机上,Win32_NetworkAdapter上的任何查询始终需要0.3秒。因此,使用此方法将使应用程序启动又增加0.3秒(或更糟的时间),我认为这是 Not Acceptable 。尤其是因为我想不出一个单一的正当理由,所以弄清楚本地计算机上的MAC地址和即插即用设备ID需要花这么长时间。

    搜索其他方法以获取MAC地址产生了 GetAdaptersInfo和更新的 GetAdaptersAddresses函数。他们没有WMI施加的0.3秒惩罚。这些函数是.NET Framework的 NetworkInterface类(通过检查.NET源代码确定)和“ipconfig”命令行工具(通过使用 Dependency Walker确定)所使用的函数。

    我在C#中做了一个简单的示例,其中列出了使用NetworkInterface类的所有网络适配器。不幸的是,使用这些API似乎有两个缺点:
  • 这些API甚至没有列出禁用的网络适配器。这意味着我无法从缓存中查找已禁用适配器的MAC地址。
  • 我看不到如何获取PNPDeviceID来过滤掉非物理适配器。

  • 我的问题是:最多可以在几十毫秒内使用什么方法来获取本地计算机的物理适配器的MAC地址(无论是否启用)?

    (我在C#和C++方面都有丰富的经验,并且可以阅读其他语言,所以我真的不在乎答案中可能使用哪种语言)。

    编辑:为了响应Alex K关于仅使用立即返回和正向返回的建议,并提供一些我正在做的示例WMI代码-这里是一些C#代码,列出了感兴趣的列:
        public static void NetTest() {
    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
    EnumerationOptions opt = new EnumerationOptions();
    // WMI flag suggestions from Alex K:
    opt.ReturnImmediately = true;
    opt.Rewindable = false;
    ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\cimv2", "select MACAddress, PNPDeviceID, ConfigManagerErrorCode from Win32_NetworkAdapter", opt);
    foreach (ManagementObject obj in searcher.Get()) {
    Console.WriteLine("=========================================");
    foreach (PropertyData pd in obj.Properties) {
    Console.WriteLine("{0} = {1}", pd.Name, pd.Value);
    }
    }
    Console.WriteLine(sw.Elapsed.TotalSeconds);
    }

    我调用了此函数3次,每次都在最后一行打印了0.36秒。因此,建议的标志似乎没有任何作用:正或负。这并不是太令人惊讶,因为 How to make forward-only, read-only WMI queries in C#?的答案似乎表明,除非存在大量记录(例如,数百到数千个),否则不会观察到性能的变化,而Win32_NetworkAdapter表不是这种情况。

    编辑2:已提出多个答案,以使用IP帮助程序API中的 SendARP(这与具有GetAdaptersInfo函数的API相同)。与GetAdaptersInfo相比,这对于查找本地MAC地址有什么优势?我想不出什么-从表面上看,GetAdaptersInfo似乎返回了比SendARP对本地适配器更彻底的信息集。现在我考虑了一下,我认为我的问题很大一部分集中在枚举的概念上:计算机上首先存在哪些适配器? SendARP不执行枚举:它假定您已经知道您想要MAC的适配器的IP地址。我需要弄清楚系统上存在哪些适配器。这引起了一些问题:
  • 如果拔下网络电缆会怎样?例如,这在笔记本电脑上会很常见(拔掉以太网,断开WiFi卡)。我尝试使用NetworkInterface.GetAllNetworkInterfaces()并在拔出媒体时使用GetIPProperties().UnicastAddresses列出了所有单播地址。 Windows没有列出地址,因此我无法想到可以传递给SendARP的任何地址。直观地讲,拔出的适配器仍将具有物理地址,但没有IP地址(因为它不在具有DHCP服务器的网络上),这是有道理的。
  • 这使我进入:如何获取要使用SendARP测试的本地IP地址列表?
  • 如何获取每个适配器的PNPDeviceID(或类似的ID,可用于过滤非物理适配器)?
  • 如何列出禁用的适配器,以便可以从缓存中查找MAC地址(即上次启用该地址时找到的MAC地址)?

  • 这些问题似乎无法由SendARP解决,这是我提出此问题的主要原因(否则,我将使用GetAdaptersInfo并继续进行操作...)。

    最佳答案

    我完全放弃了WMI,在获得所需信息的同时进行了重大改进。正如WMI所指出的,要花费超过0.30秒才能获得结果。使用我的版本,我可以在约0.01秒内获得相同的信息。

    我使用设置API,配置管理器API,然后直接在NDIS网络驱动程序上发出OID请求以获取MAC地址。设置API似乎令人讨厌,特别是在获取诸如属性值之类的东西时。必须将安装程序API调用降到最低。 (通过查看在设备管理器中加载设备的“详细信息”选项卡需要多长时间,您实际上可以看到它有多严重)。

    关于WMI为什么这么慢的猜测:我注意到WMI的Win32_NetworkAdapter总是花费相同的时间,无论我查询的是哪个属性子集。似乎WMI Win32_NetworkAdapter类的程序员很懒,并且没有像其他WMI类一样优化其类以仅收集所请求的信息。他们可能会收集所有信息,无论是否要求。他们可能极大地依赖Setup API来执行此操作,而对慢速Setup API的过多调用以获取不需要的信息是使它如此缓慢的原因。

    我所做工作的高级概述:

  • 使用SetupDiGetClassDevs获取系统上存在的所有网络设备。
  • 我过滤掉所有没有“PCI”枚举数的结果(将SPDRP_ENUMERATOR_NAME与SetupDiGetDeviceRegistryProperty一起使用以获取枚举数)。
  • 剩余部分,我可以使用CM_Get_DevNode_Status获取设备状态和错误代码。具有可移动设备状态代码的所有设备都会被过滤掉。
  • 如果设置DN_HAS_PROBLEM使得错误代码非零,则设备可能已禁用(或存在其他问题)。未加载驱动程序,因此我们无法向驱动程序发出请求。因此,在这种情况下,我将从维护的缓存中加载网卡的MAC地址。
  • 父设备可以移动,因此我也可以通过使用CM_Get_Parent和CM_Get_DevNode_Status递归检查设备树来查找父可移动设备,从而过滤掉那些父设备。
  • 其余所有设备都是PCI总线上的不可移动网卡。
  • 对于每个网络设备,我都将SetupDiGetClassDevs与GUID_NDIS_LAN_CLASS GUID和DIGCF_DEVICEINTERFACE标志一起使用以获取其接口(interface)(仅在启用该设备/没有问题的情况下才有效)。
  • 在驱动程序接口(interface)上将IOCTL_NDIS_QUERY_GLOBAL_STATS与OID_802_3_PERMANENT_ADDRESS结合使用以获取永久MAC地址。将其保存在缓存中,然后返回。

  • 结果是可以可靠地指示PC上的MAC地址,这些MAC地址应不受VMware,VirtualBox制造的“假”网卡的影响,而不受临时禁用的网卡的影响,以及不受USB,ExpressCard, PC卡或任何将来的可移动接口(interface)。

    编辑:并非所有网卡都支持IOCTL_NDIS_QUERY_GLOBAL_STATS。绝大多数都可以工作,但是有些英特尔卡却不能。参见 How to reliably and quickly get the MAC address of a network card given its device instance ID

    关于c# - 快速替换Win32_NetworkAdapter WMI类以获取本地计算机的MAC地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7322253/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com