.NET Framework 未直接提供取得網卡即時流量的類別,可透過 P/Invoke 呼叫 IpHlpApi.dll 的 GetIfTable 函式,取得 MIB_IF_TABLE 結構,包含每張網卡的位元組收發計數、速度、類型、MAC 位址等資訊。
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
public class NetworkMonitor
{
[DllImport("IpHlpApi.dll", SetLastError = true)]
private static extern uint GetIfTable(byte[] pIfTable, ref uint pdwSize, bool bOrder);
// 對應 MIB_IFROW 結構
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct MIB_IFROW
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string wszName;
public uint dwIndex;
public uint dwType;
public uint dwMtu;
public uint dwSpeed;
public uint dwPhysAddrLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] bPhysAddr;
public uint dwAdminStatus;
public uint dwOperStatus;
public uint dwLastChange;
public uint dwInOctets;
public uint dwInUcastPkts;
public uint dwInNUcastPkts;
public uint dwInDiscards;
public uint dwInErrors;
public uint dwInUnknownProtos;
public uint dwOutOctets;
public uint dwOutUcastPkts;
public uint dwOutNUcastPkts;
public uint dwOutDiscards;
public uint dwOutErrors;
public uint dwOutQLen;
public uint dwDescrLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] bDescr;
}
// 對應 MIB_IFTABLE 結構
[StructLayout(LayoutKind.Sequential)]
private struct MIB_IFTABLE
{
public uint dwNumEntries;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // 實際大小由 dwNumEntries 決定
public MIB_IFROW[] table;
}
}
public class NetInfo
{
public string Name { get; set; }
public string Description { get; set; }
public string MacAddress { get; set; }
public uint Speed { get; set; } // bps
public uint InBytes { get; set; }
public uint OutBytes { get; set; }
public OperationalStatus Status { get; set; }
}
public static List GetAllNetworkInfo()
{
List list = new List();
// 先取得緩衝區大小
uint size = 0;
uint ret = GetIfTable(null, ref size, false);
if (ret != 122) // ERROR_INSUFFICIENT_BUFFER
throw new System.ComponentModel.Win32Exception((int)ret);
byte[] buffer = new byte[size];
ret = GetIfTable(buffer, ref size, false);
if (ret != 0)
throw new System.ComponentModel.Win32Exception((int)ret);
// 將 byte[] 反序列化為結構
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
IntPtr ptr = handle.AddrOfPinnedObject();
MIB_IFTABLE table = (MIB_IFTABLE)Marshal.PtrToStructure(ptr, typeof(MIB_IFTABLE));
// 重新計算陣列實際大小
int rowSize = Marshal.SizeOf(typeof(MIB_IFROW));
table.table = new MIB_IFROW[table.dwNumEntries];
IntPtr rowPtr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(uint))); // 跳過 dwNumEntries
for (int i = 0; i < table.dwNumEntries; i++)
{
MIB_IFROW row = (MIB_IFROW)Marshal.PtrToStructure(rowPtr, typeof(MIB_IFROW));
table.table[i] = row;
rowPtr = new IntPtr(rowPtr.ToInt64() + rowSize);
}
// 轉換為友善模型
foreach (var row in table.table)
{
list.Add(new NetInfo
{
Name = Encoding.ASCII.GetString(row.wszName).TrimEnd('\0'),
Description = Encoding.ASCII.GetString(row.bDescr, 0, (int)row.dwDescrLen).TrimEnd('\0'),
MacAddress = BitConverter.ToString(row.bPhysAddr, 0, (int)row.dwPhysAddrLen).Replace('-', ':'),
Speed = row.dwSpeed,
InBytes = row.dwInOctets,
OutBytes = row.dwOutOctets,
Status = (OperationalStatus)row.dwOperStatus
});
}
}
finally
{
handle.Free();
}
return list;
}
var prev = GetAllNetworkInfo();
while (true)
{
Thread.Sleep(1000);
var curr = GetAllNetworkInfo();
for (int i = 0; i < curr.Count; i++)
{
if (curr[i].Name == prev[i].Name)
{
double downKB = (curr[i].InBytes - prev[i].InBytes) / 1024.0;
double upKB = (curr[i].OutBytes - prev[i].OutBytes) / 1024.0;
Console.WriteLine($"{curr[i].Description} ↓{downKB:F1} KB/s ↑{upKB:F1} KB/s");
}
}
prev = curr;
}
GetIfTable 僅支援 IPv4 介面,IPv6 請用 GetIfTable2 (Vista+)。System.Management 取得更豐富資訊(如 IPv6、DHCP 狀態)。
替代方案: .NET Core / .NET 5+ 可使用 System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() 取得類似資料,跨平台且無需 P/Invoke。