using System.Net.NetworkInformation; using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace MoShiro.Measurement; /// /// Windows 上读主机资源占用( 在 Linux 之外的实现):CPU 时间和物理内存用两个只读的系统调用 /// (GetSystemTimes、GlobalMemoryStatusEx),网卡流量和链路速率用 .NET 自带的 。 /// 和 Linux 一样只有汇总的数字:不读网卡名、MAC、IP,也不启动任何进程。 /// [SupportedOSPlatform("windows")] internal static partial class WindowsHost { private static readonly string[] s_virtualWords = ["Virtual", "vEthernet", "Hyper-V", "VMware", "VirtualBox", "TAP-", "Wintun", "WireGuard", "Npcap", "Bluetooth", "Loopback", "isatap", "Teredo"]; [StructLayout(LayoutKind.Sequential)] private struct FileTime { public uint Low; public uint High; public readonly long Value => (long)(((ulong)High << 32) | Low); } [StructLayout(LayoutKind.Sequential)] private struct MemoryStatus { public uint Length; public uint MemoryLoad; public ulong TotalPhys; public ulong AvailPhys; public ulong TotalPageFile; public ulong AvailPageFile; public ulong TotalVirtual; public ulong AvailVirtual; public ulong AvailExtendedVirtual; } [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool GetSystemTimes(out FileTime idle, out FileTime kernel, out FileTime user); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool GlobalMemoryStatusEx(ref MemoryStatus status); /// 系统启动以来的 CPU 总时间和空闲时间(和 Linux 的 /proc/stat 第一行同一个意思:两次采样相减得到占用)。 public static (long Total, long Idle)? Cpu() { try { if (!GetSystemTimes(out FileTime idle, out FileTime kernel, out FileTime user)) { return null; } // 内核时间里已经包含了空闲时间 return (kernel.Value + user.Value, idle.Value); } catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException) { return null; } } /// 物理内存的总量和已用(总量减去可用)。 public static (long Total, long Used)? Memory() { try { var status = new MemoryStatus { Length = (uint)Marshal.SizeOf() }; if (!GlobalMemoryStatusEx(ref status) || status.TotalPhys == 0) { return null; } return ((long)status.TotalPhys, (long)(status.TotalPhys - Math.Min(status.AvailPhys, status.TotalPhys))); } catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException) { return null; } } /// 所有物理网卡(在用的,排除回环、隧道、虚拟网卡)的累计收发字节,以及链路速率之和(Mbit/s;有一块读不到速率就是 null)。 public static (long Rx, long Tx, long? LinkMbps)? Network() { try { long rx = 0; long tx = 0; long mbps = 0; bool speedKnown = true; bool any = false; foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { if (nic.OperationalStatus != OperationalStatus.Up || nic.NetworkInterfaceType is NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel || s_virtualWords.Any(w => nic.Description.Contains(w, StringComparison.OrdinalIgnoreCase) || nic.Name.Contains(w, StringComparison.OrdinalIgnoreCase))) { continue; } IPInterfaceStatistics stats = nic.GetIPStatistics(); rx += stats.BytesReceived; tx += stats.BytesSent; any = true; if (nic.Speed > 0) { mbps += nic.Speed / 1_000_000; } else { speedKnown = false; } } return any ? (rx, tx, speedKnown && mbps > 0 ? mbps : null) : null; } catch (NetworkInformationException) { return null; } } }