wmi获取禁用网卡的mac地址

67 阅读4分钟

wmi获取禁用网卡的mac地址

  • 最近遇到一个性能问题,我们的软件启动需要验证License,验证License需要获取网卡的mac地址(无论是禁用的还是启用的)。之前的做法是运行一个进程执行 chcp 437&&powershell Get-NetAdapter
    • 这相当于先开启一个cmd 执行 chcp 437 ,然后在cmd再启动 powershell 执行 Get-NetAdapter
    • 此操作相当耗时,基本上要耗时2秒以上,这对于软件启动来说是不可接受的
    • 所以就研究了 powershell执行的 Get-NetAdapter 底层是如何实现的,是否可以在 C# 中直接复刻实现以达到减少耗时的目的。

为什么采用启动powershell的方式去获取网卡的mac地址

  • 常用的获取网卡地址的方式

    1. NetworkInterface.GetAllNetworkInterfaces获取不到禁用的网卡信息

      var list = NetworkInterface.GetAllNetworkInterfaces();
      foreach (var item in list)
      {
          var name = item.Name;
          var address = item.GetPhysicalAddress();
          Debug.WriteLine($"name: {name}, mac: {address}");
      }
      
    2. 使用wmi方式获取 Win32_NetworkAdapter获取不到禁用网卡的Mac地址

      using (var search = new ManagementObjectSearcher("select * from Win32_NetworkAdapter where PhysicalAdapter = TRUE"))
      {
          foreach(ManagementObject item in search.Get())
          {
              string name = item["Name"]?.ToString();
              string address = item["MACAddress"]?.ToString();
              Debug.WriteLine($"name: {name}, mac: {address}");
          }
      }
      
  • 基于以上两种常用的方式都无法获取到禁用网卡的信息,故最后采用绕远路的方法:启动powershell执行 get-netadapter

powershell的 get-netadapter底层做了些什么?

  • 经过查阅资料,get-netadapter 其实是调用了 Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_NetAdapter
  • 于是兴冲冲的打开 powershell 执行了以上命令,结果输出的内容很多,与 get-netadapter 输出的内容大相径庭。
  • 但是在执行完一遍get-netadapter之后,再次运行 Get-CimInstance 命令,发现输出内容确实一致
    • 这里问了chatgpt,说是get-adapter 会做一些初始化的操作,如果先执行 get-ciminstance (在没有初始化的前提下),会输出一些不正确的内容。
  • get-ciminstance 命令其实就是调用的 wmi

什么是wmi

  • wmi: windows management instruments windows管理规范,提供了系统信息访问的api

  • 一句话总结:wmi=windows的系统级数据库,你可以通过wql语句查看和控制系统

  • 那cim又是什么东西?

    • wmi是基于 cim 实现的,common infomation model, 意思就是将系统资源抽象成类
    • 我们可以通过查询语句去访问这些资源类对象的信息
  • 微软将系统资源抽象成类,把这些类放在不同的命名空间下(默认的命名空间为root\CIMV2),并且将类的名称添加了前缀,具体划分规则如下

    类名前缀来源典型命名空间举例
    Win32_传统 WMI 类(早期,兼容旧系统)root\CIMV2Win32_Process, Win32_NetworkAdapter
    MSFT_新一代 CIM 类(基于 PowerShell 和 DMTF 标准)root\StandardCimv2MSFT_NetAdapter, MSFT_Disk, MSFT_NetIPAddress
    CIM_标准 DMTF(跨平台)类root\CIMV2CIM_LogicalDisk, CIM_ComputerSystem
    自定义(厂商名)第三方 / OEM 驱动提供root\WMIroot\<vendor>Intel_SMBIOSData, Dell_BIOSProvider
  • windows界面的设备管理器有一部分的实现就是基于 wmi

  • 类名描述
    Win32_Process表示系统中的一个进程
    Win32_Service表示一个 Windows 服务
    MSFT_NetAdapter表示一个网络适配器
    Win32_LogicalDisk表示一个逻辑磁盘(C盘、D盘等)

如何使用wmi呢?

  • powershell
    • 访问 Win32_NetworkAdapter类对象: get-ciminstance -classname win32_networkadapter
  • C#: 上面已经讲过

解决方案

  • 既然已经知道powershell的命令get-netadapter 底层是 Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_NetAdapter,那么我们就可以应用到c#上
using (var search = new ManagementObjectSearcher("root/standardcimv2", "select * from msft_netadapter"))
{
    foreach (ManagementObject item in search.Get())
    {
        string name = item["Name"]?.ToString();
        string address = item["MacAddress"]?.ToString();
        Debug.WriteLine($"name: {name}, mac: {address}");
    }
}
  • 但是!!!运行报错了,提示没有 MacAddress这个字段,又是经过了一番尝试,发现原来这里是 PermanentAddress,可能是powershell 为了显示的更准确,自行改了字段的名称

扩展

走的歪路:使用c++调用wmi获取系统信息

  • 之前做了很多尝试依旧不得入门,无法在C#中获取到禁用网卡的mac地址,于是转而让chatgpt给个c++的方案,虽然给的代码很多编译报错,运行报错,最后终于运行起来一次(连续运行第二次就崩溃),好歹成功获取到了,也就是这里发现读取的是 PermanentAddress 字段,而不是 MacAddress,最终找到了在C#中的正确方案。
    • 在得到正确方案后,也就没有再研究c++方案为什么运行第二次就会崩溃的问题,空下来再说(自我安慰,基本上是不会再去看那段代码了)