给定网卡的设备实例ID,我想知道它的MAC地址。在我的系统上集成Intel Gigabit卡的设备实例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
以获得设备接口。SetupDiGetDeviceInterfaceDetail
。这使我们以字符串的形式获得设备路径:\\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}
CreateFile
打开它。IOCTL_NDIS_QUERY_GLOBAL_STATS
和OID_802_3_PERMANENT_ADDRESS
的OID调用IOCTL_NDIS_QUERY_GLOBAL_STATS
来获取MAC地址。这通常是可行的,并已成功地在相当多的机器上使用。但是,似乎很少有机器的网络驱动程序没有正确响应步骤6中的DeviceIoControl
请求;即使在将网卡驱动程序更新为最新的驱动程序之后,问题仍然存在。这些是更新的、基于Windows 7的计算机.具体来说,DeviceIoControl
成功完成,但是返回零字节,而不是包含MAC地址的预期的6个字节。
IOCTL_NDIS_QUERY_GLOBAL_STATS
的MSDN页面上似乎有一条线索
这个IOCTL将在以后的操作系统版本中被废弃。您应该使用WMI接口来查询小型端口驱动程序信息。有关更多信息,请参见NDIS对WMI的支持。
--也许较新的网卡驱动程序不再执行此IOCTL?
那么,我该怎么做呢?我的方法有可能有疏忽,我做了一些轻微的错误吗?还是我需要采取一种更不同的方法?一些替代办法似乎包括:
Win32_NetworkAdapter
WMI类:提供所需的信息,但由于性能糟糕而被拒绝。请参阅用于获取本地计算机MAC地址的NetworkAdapter WMI类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}
的字符串,与WMI类返回的DeviceName
相同。因此,我不知道如何将它与设备实例ID联系起来,我也不确定它是否能100%地工作--例如,对于没有配置TCP/IP协议的适配器。如果我能找到一种方法从设备实例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返回非零表示成功,但随后返回零字节.
发布于 2012-10-01 15:37:17
最后,我使用SetupDiGetDeviceRegistryProperty
来阅读SPDRP_FRIENDLYNAME
。如果没有找到,那么我会阅读SPDRP_DEVICEDESC
。最终,这会给我一个字符串,比如"VirtualBox主机-只有以太网适配器#2“。然后,我将其与WMI类中的InstanceName属性(MSNdis_EthernetPermanentAddress
WMI类)进行匹配。如果有多个适配器共享同一个驱动程序(即"#2“、"#3”等),则必须读取这两个属性--如果只有一个适配器,则SPDRP_FRIENDLYNAME
不可用,但如果有多个适配器,则需要SPDRP_FRIENDLYNAME
来区分它们。
这个方法让我有点紧张,因为我在比较似乎是本地化的字符串,而且我没有发现任何文档可以保证我所做的工作总是有效的。不幸的是,我也没有找到任何更好的方法来工作。
其他几种替代方法包括在无文档的注册表位置中卑躬屈膝。一个方法是spencercw的方法,另一个方法是读取SPDRP_DRIVER
,它是HKLM\SYSTEM\CurrentControlSet\Control\Class
下一个子键的名称。在驱动键下面,查找Linkage\Export
值,该值看起来可能与MSNdis_EnumerateAdapter
类的DeviceName
属性相匹配。但我没有发现任何文件表明这些值可以合法匹配。此外,我找到的关于Linkage\Export
的唯一文档是来自Win2000注册表引用,并明确表示应用程序不应该依赖它。
另一种方法是查看我最初的问题,步骤4:“返回的设备接口的SetupDiGetDeviceInterfaceDetail
”。设备接口路径实际上可以用于重建设备路径。从设备接口路径开始:\\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}
。然后,删除最后斜杠之前的所有内容,留下:{28fd5409-15bd-4c06-b62f-004d3a06f852}
。最后,将\Device\
添加到此字符串,并将其与WMI类匹配。然而,这似乎是无文档化的,并且依赖于设备接口路径的实现细节。
最后,我所研究的其他方法都有它们自己的未记录的复杂情况,这听起来至少和匹配SPDRP_FRIENDLYNAME
/ SPDRP_DEVICEDESC
字符串一样严重。因此,我选择了更简单的方法,即只将这些字符串与WMI类匹配。
发布于 2012-09-27 17:26:36
有一种方法可以做到:
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
读取注册表项(如果存在的话)(这是从这里获得的;在谷歌上搜索该键似乎有很好的文档记录,因此不太可能更改)PCI\VEN_14E4&DEV_16B1&SUBSYS_96B11849&REV_10\4&2B8260C3&0&00E4
)IP_ADAPTER_ADDRESSES
并查看PhysicalAddress
字段如果没有一百万种方法来做某事,那就不是Windows了!
发布于 2019-07-11 12:20:31
我想您希望获得MAC地址,以便实现某种DRM、库存或分类系统,因为您试图获得永久的MAC地址而不是当前的MAC地址。
您似乎忘记了,甚至还有一个管理上附加的MAC地址(换句话说:一个“强制的”MAC地址)。
一些驱动程序允许您在设备属性页的Advanced (例如:我的Marvell网络适配器允许我这样做)下执行此操作,而另一些驱动程序则不允许您这样做(请阅读:他们不支持该属性)。
但是,它的结尾都是一个Registry:HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress
,带有一个REG_SZ
类型。在这里,您可以设置一个与原始地址不同的MAC地址,格式为“01020304abc d”(6个字节,普通十六进制,没有:
分隔符或0x
前缀)。设置好后,重新启动机器,在打开电源时,新的MAC地址将生效。
我碰巧有一个主板与两个Marvell集成NIC,和一个NETGEAR。Marvell支持更改MAC地址:如果在注册表中设置NetworkAddress
值,也可以在驱动程序属性页中看到新值,并且它立即生效,无需重新启动(如果您从设备属性页中更改它)。下面是用不同方法读取MAC地址的结果:
GetAdaptersInfo
:新的MAC地址IOCTL_NDIS_QUERY_GLOBAL_STATS
:原始MAC地址MSNdis_EthernetPermanentAddress
:原始MAC地址我尝试在NETGEAR的注册表中添加NetworkAddress
值,结果如下:
GetAdaptersInfo
:新的MAC地址IOCTL_NDIS_QUERY_GLOBAL_STATS
:新的MAC地址MSNdis_EthernetPermanentAddress
:新的MAC地址原来的MAC地址不见了。
因此,为了不被“恶意”用户愚弄,您总是需要检查HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress
注册表值。如果设置好了,我想最好完全不信任Network,因为需要由驱动程序实现来决定使用不同方法提供给您的是什么。
获取注册表密钥的背景信息:
有关HKLM\SYSTEM\CurrentControlSet\Class键的Microsoft文档
根据该页面上的Microsoft文档,
每个类都有一个子项,它使用安装类的GUID命名。
因此,我们选择了{4D36E972-E325-11CE-BFC1-08002BE10318}
子项(又名GUID_DEVCLASS_NET
,在<devguid.h>
中定义,并进一步记录了这里)。
同样,根据微软的文档,
每个类子项包含系统中安装的该类的每个设备实例的称为软件密钥(或,驱动键)的其他子项。这些软件键中的每一个都是使用设备实例ID命名的,它是一个基数-10,4位序数值,xxxx部分是从0开始的一个正整数的4字符文本表示形式。
因此,您可以遍历从0000,0001,0002到系统中的网络适配器数量的子项。
文档在这里结束:我没有找到任何关于不同注册表值的其他文档,或者类似的文档。
但是,在每个子项中,您都可以找到REG_SZ值,这些值可以帮助您链接GetAdaptersInfo()
、MSNdis_EthernetPermanentAddress
、Win32_NetworkAdapter
和设备实例ID世界(这回答了您的问题)。
书记官处的价值是:
DeviceInstanceID
:它的值是,毫不奇怪,设备实例IDNetCfgInstanceId
:它的值是IP_ADAPTER_INFO
结构的AdapterName
成员,由GetAdaptersInfo()
返回。它也是GUID
WMI类的成员。NetworkAddress
地址:如果这里存在有效的MAC地址,驱动程序可以将其报告为GetAdaptersInfo()
、MSNdis_EthernetPermanentAddress
和IOCTL_NDIS_QUERY_GLOBAL_STATS
使用的MAC地址!然后,正如您已经说过的,MSNdis_EthernetPermanentAddress
WMI类与"world“的其他部分之间的唯一连接是它的InstanceName
成员。您可以将其关联到IP_ADAPTER_INFO
结构的IP_ADAPTER_INFO
成员(由GetAdaptersInfo()
返回)。虽然它可能是一个本地化的名称,但它对于系统似乎是唯一的(对于我的两个集成的Marvell NIC,第二个在它的名称后面有一个“#2”)。
最后说明:
以上所说的,用户可以选择禁用WMI..。
https://stackoverflow.com/questions/12608757
复制相似问题