这是完全可能的!
昨天遇到完全相同的问题并成功解决。
基本上我们得到了另一种接口类型,因为 Hyper-V 在创建虚拟交换机时做了一些桥接工作。这就是为什么当一个迭代时NetworkInterface.GetAllNetworkInterfaces()
根本没有 wlan 接口,并且具有相同 IP 地址的新接口是一个虚拟接口,它也参与了带有无线适配器的网桥。
所以我们需要做以下事情:
- 确定实际的硬件接口
- 确定其实际类型
确定实际的硬件接口
Windows 中的网络接口以类似堆栈的方式组织。也就是说,一些接口可以位于其他接口之上,并且可能实际的硬件接口将位于这些东西的底部。
堆栈本身由一个记录数组表示,例如
[StructLayout(LayoutKind.Sequential)]
public struct MIB_IFSTACK_ROW
{
public uint HigherLayerInterfaceIndex;
public uint LowerLayerInterfaceIndex;
}
whereHigherLayerInterfaceIndex
严格高于LowerLayerInterfaceIndex
。有了这些信息和源接口索引,就可以很容易地确定实际的硬件接口索引:
- 将当前索引设置为源接口索引
- 虽然有一条
HigherLayerInterfaceIndex
等于当前索引的记录 - 将当前索引设置LowerLayerInterfaceIndex
为该记录并再次重复
- 如果没有
HigherLayerInterfaceIndex
等于当前索引的记录 - 当前索引是结果
使用 WinAPI 方法GetIfStackTable以检索网络接口堆栈信息。以下 P/Invoke 定义可能会有所帮助:
[StructLayout(LayoutKind.Sequential)]
public struct MIB_IFSTACK_ROW
{
public uint HigherLayerInterfaceIndex;
public uint LowerLayerInterfaceIndex;
}
[StructLayout(LayoutKind.Sequential)]
public struct MIB_IFSTACK_TABLE
{
public uint NumEntries;
public MIB_IFSTACK_ROW Table;
}
[DllImport("iphlpapi.dll", CallingConvention = CallingConvention.StdCall)]
public static extern Win32Error GetIfStackTable(out IntPtr table);
[DllImport("iphlpapi.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void FreeMibTable(IntPtr memory);
由于(显然)记录计数可能因调用而异,因此必须首先从 IntPtr 读取计数(uint32),然后逐个记录编组。下面的代码应该有助于理解这个想法(尽管由于我们内部的东西它不会编译):
public static IEnumerable<MIB_IFSTACK_ROW> EnumIfStackTable()
{
var error = GetIfStackTable(out var table);
error.ToHRESULT().ThrowIfFailed();
Contract.Assert(table != null);
Contract.Assert(!table.IsInvalid);
using (table)
{
var pointer = table.DangerousGetHandle();
var entries = (uint)Marshal.ReadInt32(pointer);
var rowPointer = IntPtr.Add(pointer, IfStackTableOffset);
for (var i = 0; i < entries; i++)
{
yield return (MIB_IFSTACK_ROW)Marshal.PtrToStructure(rowPointer, typeof(MIB_IFSTACK_ROW));
rowPointer = IntPtr.Add(rowPointer, IfStackRowSize);
}
}
}
表偏移量和行大小是这样计算的:
internal static readonly int IfStackRowSize = Marshal.SizeOf(typeof(MIB_IFSTACK_ROW));
internal static readonly int IfStackTableOffset = (int)Marshal.OffsetOf(typeof(MIB_IFSTACK_TABLE), nameof(MIB_IFSTACK_TABLE.Table));
FreeMibTable
完成接口堆栈后不要忘记调用。
确定硬件接口类型
有几个地方我们可以尝试找到具有索引的接口类型,但最可靠的方法是直接从 WinAPI 获取此信息。我建议使用GetIfTable2 WinAPI 函数——它允许我们枚举系统中存在的每个网络接口,并检查它的属性。不幸的是,这次 P/Invoke 对于这个答案来说太大了,但主要思想(和编组)与上一段中的相同。
拥有MIB_IF_ROW2结构的数组,可以通过迭代此类数组并将InterfaceIndex
属性与所需的接口索引进行比较来轻松找到所需的接口。当找到接口时 - 只需获取Type
属性值并将其转换为NetworkInterfaceType
- 就是这样,你太棒了!
为什么不使用其他方法:
- NetworkInterface.GetAllNetworkInterfaces() 可能不会返回我们想要的接口
- WMI Win32_NetworkAdapter - 可能仍然显示错误的类型
- WMI MSFT_NetAdapter - 显示正确的适配器类型,但至少需要 Windows 8
有用的链接