鉴于网卡的device instance ID,我想知道它的MAC地址。集成Intel千兆卡的系统上的设备实例ID示例:
PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
到目前为止,我使用的算法的工作原理如下:
DIGCF_DEVICEINTERFACE
致电SetupDiGetClassDevs
。SetupDiEnumDeviceInfo
以获取SP_DEVINFO_DATA
。GUID_NDIS_LAN_CLASS
致电SetupDiEnumDeviceInterfaces
以获取设备界面。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
打开它
DeviceIoControl
和IOCTL_NDIS_QUERY_GLOBAL_STATS
的OID呼叫OID_802_3_PERMANENT_ADDRESS
以获取MAC地址。这通常有效,并已在相当多的机器上成功使用。但是,似乎很少有机器的网络驱动程序在步骤#6中没有正确响应DeviceIoControl
请求;即使将网卡驱动程序更新到最新版本后问题仍然存在。这些是较新的基于Windows 7的计算机。具体来说,DeviceIoControl
成功完成,但返回零字节而不是包含MAC地址的预期六个字节。
一条线索似乎出现在IOCTL_NDIS_QUERY_GLOBAL_STATS
的MSDN页面上:
此IOCTL将在以后的操作系统版本中弃用。您 应该使用WMI接口来查询微型端口驱动程序信息。对于 更多信息请参阅NDIS对WMI的支持。
- 也许更新的网卡驱动程序不再实现这个IOCTL了?
那么,我应该怎么做呢?是否有可能在我的方法中存在疏忽并且我做了一些稍微错误的事情?或者我需要采取更加不同的方法吗?一些替代方法似乎包括:
Win32_NetworkAdapter
WMI类:提供所需信息但由于性能糟糕而被拒绝。请参阅Fast replacement for Win32_NetworkAdapter WMI class for getting MAC address of local computer 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 NDIS类返回的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返回非零表示成功,但随后返回零字节。
答案 0 :(得分:4)
这是一种方法:
GetAdaptersAddresses
以获取IP_ADAPTER_ADDRESSES
结构列表AdapterName
字段获取其GUID(我不确定这种行为是否得到保证,但我的系统中的所有适配器都有GUID,文档说明{{ 1}}是永久性的)AdapterName
读取注册表项(如果存在)(从here获得此想法;在Google上搜索该键似乎已有详细记录,因此不太可能更改) 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
字段如果没有百万种方法可以做某事,那就不是Windows了!
答案 1 :(得分:2)
我最后使用SetupDiGetDeviceRegistryProperty
阅读SPDRP_FRIENDLYNAME
。如果找不到,那我就读了SPDRP_DEVICEDESC
。最终,这给我一个像“VirtualBox Host-Only Ethernet Adapter#2”这样的字符串。然后我将它与WMI NDIS类(MSNdis_EthernetPermanentAddress
WMI类)中的InstanceName属性进行匹配。如果有多个适配器共享相同的驱动程序(即“#2”,“#3”等),则必须读取这两个属性 - 如果只有一个适配器,则SPDRP_FRIENDLYNAME
不可用,但如果有不止一个,然后需要SPDRP_FRIENDLYNAME
来区分它们。
这个方法让我有点紧张,因为我正在比较看似本地化的字符串,而且没有我发现的文档可以保证我正在做的事情总能奏效。不幸的是,我还没有找到任何更好的方法来记录工作。
其他一些替代方法涉及在未记录的注册表位置进行groveling。一种方法是spencercw的方法,另一种方法是读取SPDRP_DRIVER
,这是HKLM\SYSTEM\CurrentControlSet\Control\Class
下的子项的名称。在驱动程序密钥下方,查找Linkage\Export
值,然后它似乎可以与DeviceName
类的MSNdis_EnumerateAdapter
属性匹配。但是没有我能找到的文件说这些价值可以合法匹配。此外,我发现的关于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 NDIS类匹配。但是,这似乎没有记录,并且依赖于设备接口路径的实现细节。
最后,我调查的其他方法都有自己的无证并发症,听起来至少与匹配SPDRP_FRIENDLYNAME
/ SPDRP_DEVICEDESC
字符串一样严重。所以我选择了更简单的方法,即将这些字符串与WMI NDIS类匹配。
答案 2 :(得分:0)
我想您想获取MAC地址以便实施某种DRM,清单或分类系统,因为您试图获取永久 MAC地址而不是当前的MAC地址。 / p>
您似乎忘记了甚至还有一个管理员叠加的MAC地址(换句话说:“强制” MAC地址)。
一些驱动程序允许您从“设备属性”页面的“高级”选项卡下执行此操作(例如:我的Marvell网络适配器允许我执行此操作),而另一些驱动程序不允许您执行此操作(请阅读:它们不支持该属性) )。
但是,所有操作都以HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress
类型的注册表值REG_SZ
结尾。
您可以在此处设置与原始MAC地址不同的MAC地址,格式为“ 01020304abcd”(6个字节,纯十六进制,不带:
分隔符或0x
前缀)。
设置完后,重新启动计算机,并在开机时新的MAC地址生效。
我碰巧有一个带有两个Marvell集成NIC和一个NETGEAR USB WiFi NIC的主板。 Marvell一个支持更改MAC地址:如果您在注册表中设置了NetworkAddress
值,那么您也可以在驱动程序属性页中看到新值,并且该新值将立即生效,而无需重新启动(如果您需要从设备属性页更改它)。
以下是使用不同方法读取MAC地址的结果:
GetAdaptersInfo
:新的MAC地址IOCTL_NDIS_QUERY_GLOBAL_STATS
:原始MAC地址MSNdis_EthernetPermanentAddress
:原始MAC地址我尝试在NETGEAR USB WiFi NIC的注册表中添加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
注册表值。如果设置了该设置,我想最好完全不相信该网络适配器,因为由驱动程序实现决定使用不同方法呈现给您的内容。
获取该注册表项的一些背景:
Microsoft documentation about the HKLM\SYSTEM\CurrentControlSet\Class key
根据该页面上的Microsoft文档,
每个类都有一个子项,该子项使用 设置类
因此,我们选择{4D36E972-E325-11CE-BFC1-08002BE10318}
子项(又名GUID_DEVCLASS_NET
,在<devguid.h>
中定义,并进一步记录在here中)
再次,根据Microsoft文档,
每个类子项都包含其他子项,这些子项被称为软件密钥(或驱动程序密钥),用于系统中安装的该类的每个设备实例。这些软件密钥中的每一个都通过使用设备实例ID来命名,该设备实例ID是以10为底的四位数序号值 xxxx 部分是从0开始的4个字符的正整数文本表示形式。
因此,您可以遍历子键,范围从0000、0001、0002到系统中的网络适配器数。
文档到此为止:我没有找到其他有关不同注册表值的文档。
但是,在每个子项中,您都可以找到REG_SZ值,这些值可以帮助您链接GetAdaptersInfo()
,MSNdis_EthernetPermanentAddress
,Win32_NetworkAdapter
和设备实例ID世界(这将回答您的问题)。问题)。
注册表值是:
DeviceInstanceID
:毫无疑问,它的值是设备实例ID NetCfgInstanceId
:其值为AdapterName
返回的IP_ADAPTER_INFO
结构的GetAdaptersInfo()
成员。它也是GUID
WMI类的Win32_NetworkAdapter
成员。NetworkAddress
:如果此处存在有效的MAC地址,驱动程序可以将其报告为GetAdaptersInfo()
,{{ 1}}和MSNdis_EthernetPermanentAddress
!然后,正如您已经说过的那样,IOCTL_NDIS_QUERY_GLOBAL_STATS
WMI类与“世界”其余部分之间的唯一连接是由其MSNdis_EthernetPermanentAddress
成员组成的。您可以将其与InstanceName
返回的Description
结构的IP_ADAPTER_INFO
成员相关。尽管它可能是本地化的名称,但对于系统来说似乎是唯一的(对于我的两个集成式Marvell NIC,第二个在其名称后附加一个“#2”)。