wmi获取禁用网卡的mac地址
- 最近遇到一个性能问题,我们的软件启动需要验证
License,验证License需要获取网卡的mac地址(无论是禁用的还是启用的)。之前的做法是运行一个进程执行chcp 437&&powershell Get-NetAdapter- 这相当于先开启一个
cmd执行chcp 437,然后在cmd再启动powershell执行Get-NetAdapter - 此操作相当耗时,基本上要耗时2秒以上,这对于软件启动来说是不可接受的
- 所以就研究了 powershell执行的
Get-NetAdapter底层是如何实现的,是否可以在C#中直接复刻实现以达到减少耗时的目的。
- 这相当于先开启一个
为什么采用启动powershell的方式去获取网卡的mac地址
-
常用的获取网卡地址的方式
-
NetworkInterface.GetAllNetworkInterfaces:获取不到禁用的网卡信息csharpvar list = NetworkInterface.GetAllNetworkInterfaces(); foreach (var item in list) { var name = item.Name; var address = item.GetPhysicalAddress(); Debug.WriteLine($"name: {name}, mac: {address}"); } -
使用wmi方式获取
Win32_NetworkAdapter:获取不到禁用网卡的Mac地址csharpusing (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(在没有初始化的前提下),会输出一些不正确的内容。
- 这里问了chatgpt,说是
get-ciminstance命令其实就是调用的wmi
什么是wmi
-
wmi:
windows management instrumentswindows管理规范,提供了系统信息访问的api -
一句话总结:wmi=windows的系统级数据库,你可以通过wql语句查看和控制系统
-
那cim又是什么东西?
- wmi是基于
cim实现的,common infomation model, 意思就是将系统资源抽象成类 - 我们可以通过查询语句去访问这些资源类对象的信息
- wmi是基于
-
微软将系统资源抽象成类,把这些类放在不同的命名空间下(默认的命名空间为
root\CIMV2),并且将类的名称添加了前缀,具体划分规则如下类名前缀 来源 典型命名空间 举例 Win32_ 传统 WMI 类(早期,兼容旧系统) root\CIMV2Win32_Process,Win32_NetworkAdapterMSFT_ 新一代 CIM 类(基于 PowerShell 和 DMTF 标准) root\StandardCimv2MSFT_NetAdapter,MSFT_Disk,MSFT_NetIPAddressCIM_ 标准 DMTF(跨平台)类 root\CIMV2CIM_LogicalDisk,CIM_ComputerSystem自定义(厂商名) 第三方 / OEM 驱动提供 root\WMI、root\<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#上
csharp
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++方案为什么运行第二次就会崩溃的问题,空下来再说(自我安慰,基本上是不会再去看那段代码了)