Fast replacement for Win32_NetworkAdapter WMI class for getting MAC address of local computer

前端 未结 2 741
刺人心
刺人心 2021-01-03 08:29

TL;DR version of this question: WMI Win32_NetworkAdapter class contains information I need, but is too slow. What\'s a faster method of getting information for the MACAddre

2条回答
  •  被撕碎了的回忆
    2021-01-03 09:21

    I wound up cutting WMI completely out of the equation and made a significant improvement while still getting the information I wanted. As noted with WMI, it was taking > 0.30 seconds to get results. With my version, I can get the same information in about 0.01 seconds.

    I used the setup API, configuration manager API, and then made OID requests directly on the NDIS network driver to get the MAC address. The setup API seems obnoxiously slow, especially when getting things like property values. It's imperative to keep setup API calls at a minimum. (You can actually see just how bad it is by looking at how long it takes to load the "Details" tab of a device in Device manager).

    A guess on why WMI was so slow: I noted that WMI's Win32_NetworkAdapter always took the same amount of time no matter which subset of properties I queried. Seems like the WMI Win32_NetworkAdapter class programmers were lazy and didn't optimize their class to only gather requested information like other WMI classes do. They probably gather all information, whether requested or not. They probably significantly rely on Setup API to do this, and the excessive calls to the slow Setup API to get unwanted information is what makes it so slow.

    High level overview of what I did:

    1. Use SetupDiGetClassDevs to get all network devices that are present on the system.
    2. I filter out all results that don't have an enumerator of "PCI" (use SetupDiGetDeviceRegistryProperty with SPDRP_ENUMERATOR_NAME to get the enumerator).
    3. For the remainder, I can use CM_Get_DevNode_Status to get the device status and error code. All devices with removable device status codes are filtered out.
    4. If DN_HAS_PROBLEM is set such that there is a non-zero error code then the device is probably disabled (or has some other problem). The driver isn't loaded so we can't make a request to the driver. Therefore in this case I load the MAC address for the network card from a cache I maintain.
    5. A parent device could be removable so I filter those out too by recursively examining the device tree using CM_Get_Parent and CM_Get_DevNode_Status to look for parent removable devices.
    6. Any remaining devices are nonremovable network cards on the PCI bus.
    7. For each network device, I use SetupDiGetClassDevs with GUID_NDIS_LAN_CLASS GUID and DIGCF_DEVICEINTERFACE flag to get its interfaces (this only works if the device is enabled / does not have a problem).
    8. Use IOCTL_NDIS_QUERY_GLOBAL_STATS with OID_802_3_PERMANENT_ADDRESS on the driver's interface to get the permanent MAC address. Save it in the cache, and then return.

    The result is a robust indication of MAC addresses on the PC that should be immune to "fake" network cards made by VMware, VirtualBox, largely immune to network cards that are temporarily disabled, and immune to transient network cards attached via USB, ExpressCard, PC Card, or any future removable interface.

    EDIT: IOCTL_NDIS_QUERY_GLOBAL_STATS isn't supported by all network cards. The vast majority work, but some Intel cards do not. See How to reliably and quickly get the MAC address of a network card given its device instance ID

提交回复
热议问题