C# CPU & Disk Monitoring Without Polling PerformanceCounters
Event-driven C# CPU usage and disk space monitoring on Windows 10 using .NET EventCounters and WMI events like Win32_VolumeChangeEvent. Get 90% CPU threshold alerts without constant polling for efficient resource tracking.
How can I monitor CPU usage and disk space in C# on Windows 10 without polling PerformanceCounters? Is there a way to receive events when processor core time exceeds a specific threshold, such as 90%, instead of checking every second?
In C# on Windows 10, you can monitor CPU usage and disk space without polling PerformanceCounters by leveraging .NET EventCounters for low-overhead metrics and WMI events like Win32_VolumeChangeEvent for disk notifications. EventCounters use callbacks to report processor load or free disk space at configurable intervals, letting you trigger alerts when CPU core time hits 90% via a custom EventListener—no constant second-by-second checks needed. WMI handles disk space thresholds reactively on changes, though pure event-driven 90% CPU alerts require wrapping these tools in your own logic for efficiency.
Contents
- Why Skip Polling PerformanceCounters for C# CPU Monitoring and Disk Space
- EventCounters: Low-Overhead CPU Usage and Disk Space Tracking
- WMI Events for Disk Space Changes on Windows 10
- Win32_VolumeChangeEvent for Reactive Disk Notifications
- Avoiding WMI High CPU Usage in Process and Resource Watching
- Custom 90% CPU Threshold Events Without Constant Polling
- Best Practices and Alternatives for Efficient C# Monitoring
- Sources
- Conclusion
Why Skip Polling PerformanceCounters for C# CPU Monitoring and Disk Space
Polling PerformanceCounters every second works, but it chews CPU cycles and feels clunky for modern apps. Developers often hit walls with high overhead, especially on Windows 10 where WMI queries or counter loops spike processor load themselves. The smarter path? Event-driven alternatives baked into .NET and Windows APIs.
Think about it: your app shouldn’t wake up every tick just to peek at metrics. Microsoft’s diagnostics guidance pushes EventCounters as a lightweight replacement—they handle the polling internally at optimized rates, pushing data out for you to react to. Pair that with WMI’s intrinsic events for disks, and you’ve got real-time awareness without the grind.
This approach shines for services or tools tracking CPU usage per core or disk space dips. No more Timer.Tick nightmares.
EventCounters: Low-Overhead CPU Usage and Disk Space Tracking
EventCounters changed the game for C# CPU monitoring. They’re designed for diagnostics without the bloat of old-school counters. You define a counter, hook a callback for fresh data, and let the runtime push updates—perfect for processor load or disk space without your code looping.
Here’s a quick setup for CPU usage across cores:
using System.Diagnostics.Metrics;
using System.Diagnostics.Tracing;
using System.IO;
public class Metrics
{
private static Meter s_meter = new("MyAppMetrics");
private static PollingCounter<double> s_cpuCounter;
private static PollingCounter<long> s_diskCounter;
public static void Initialize()
{
// CPU usage callback: average % across cores
s_cpuCounter = s_meter.CreatePollingCounter("cpu-usage",
() =>
{
var cpu = 0.0;
using var process = Process.GetCurrentProcess();
// Or use Environment.ProcessorCount for total
cpu = process.TotalProcessorTime.TotalMilliseconds / Environment.ProcessorCount;
return cpu > 90 ? cpu : 0; // Flag high usage
});
// Disk free space callback (e.g., C: drive)
s_diskCounter = s_meter.CreatePollingCounter("free-disk-space-bytes",
() => DriveInfo.GetDrives()
.Where(d => d.IsReady && d.DriveType == DriveType.Fixed)
.Sum(d => d.TotalFreeSpace));
}
}
Subscribe with an EventListener to catch pushes:
public class MyEventListener : EventListener
{
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventName == "cpu-usage" && eventData.Payload[0] is double cpu && cpu > 90)
{
Console.WriteLine($"Alert: CPU at {cpu}%!");
// Fire your custom event here
}
}
}
This runs at 1-second intervals by default but sips resources compared to manual PerformanceCounters. Disk space updates on demand—scale it for multiple drives. Why does it beat polling? The runtime optimizes collection, and you react only on meaningful changes.
WMI Events for Disk Space Changes on Windows 10
WMI steps in where counters falter, especially for disk space. Query Win32_LogicalDisk for free space, but don’t poll—subscribe to events for changes like low space or volume mounts.
Basic WMI disk query (one-off or event-triggered):
using System.Management;
var query = new WqlEventQuery("SELECT * FROM Win32_LogicalDisk WHERE FreeSpace < 1073741824"); // <1GB
using var watcher = new ManagementEventWatcher(query);
watcher.EventArrived += (sender, e) =>
{
var disk = e.NewEvent["TargetInstance"] as ManagementBaseObject;
var freeSpace = (ulong)disk["FreeSpace"];
Console.WriteLine($"Disk low: {freeSpace / (1024*1024)} MB free");
};
watcher.Start();
This fires on space drops below your threshold. No endless loops—WMI pushes intrinsic events. But heads up: broad queries can indirectly hike CPU if unfiltered.
Win32_VolumeChangeEvent for Reactive Disk Notifications
Want pure reactivity for disks? Win32_VolumeChangeEvent delivers. It notifies on drive arrivals, removals, or mounts—ideal for space monitoring without baseline polling.
Subscribe like this:
var volumeQuery = new WqlEventQuery("SELECT * FROM Win32_VolumeChangeEvent");
using var volumeWatcher = new ManagementEventWatcher(volumeQuery);
volumeWatcher.EventArrived += (sender, e) =>
{
var driveName = e.NewEvent["DriveName"]?.ToString();
var eventType = e.NewEvent["EventType"]?.ToString();
if (eventType == "2") // Device arrival
{
// Check space on new drive
var spaceQuery = new ObjectQuery($"SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='{driveName}'");
using var searcher = new ManagementObjectSearcher(spaceQuery);
foreach (ManagementObject disk in searcher.Get())
{
Console.WriteLine($"New drive {driveName}: {(ulong)disk["FreeSpace"] / (1024*1024*1024)} GB free");
}
}
};
Pairs perfectly with CPU tracking. EventType values (1=Removal, 2=Arrival) keep it targeted. Windows 10 handles this natively—no extra installs.
Avoiding WMI High CPU Usage in Process and Resource Watching
Ever notice WMI eating processor load? Stack Overflow devs nailed it: event subscriptions like __InstanceCreationEvent poll under the hood inefficiently. Filters help—narrow to specific processes or disks.
Tweak queries: SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfDisk_LogicalDisk' AND TargetInstance.FreeSpace < 500000000. The WITHIN clause batches checks, cutting CPU spikes.
Switch to ETW for extreme cases: EventProvider traces low-level disk I/O and CPU without WMI overhead. But for most C# apps, refined WMI plus EventCounters covers it.
Custom 90% CPU Threshold Events Without Constant Polling
No built-in WMI for “CPU >90%” events—it’s poll-or-bust there. But EventCounters bridge the gap beautifully. Extend the listener:
var listener = new MyEventListener();
listener.EnableEvents(EventSource.FindNameByDisplayName("MyAppMetrics"), EventLevel.Informational);
public class ThresholdEventArgs : EventArgs
{
public double CpuUsage { get; set; }
public string Message { get; set; }
}
public static event EventHandler<ThresholdEventArgs> CpuThresholdExceeded;
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.Payload[0] is double cpu && cpu >= 90)
{
CpuThresholdExceeded?.Invoke(this, new ThresholdEventArgs { CpuUsage = cpu, Message = "High CPU alert!" });
}
}
Tune polling to 500ms if needed (listener.SetBaseInterval(500)). This feels event-driven: your code sleeps until the runtime shouts. Combine with disk watchers for full coverage.
Best Practices and Alternatives for Efficient C# Monitoring
Keep overhead low: Filter WMI ruthlessly, use async event handlers, and offload to background services. Test on Windows 10—Task Manager will show if you’re clean.
Alternatives? ETW via Microsoft.Diagnostics.Tracing for kernel-level CPU traces, or Prometheus exporters for .NET. But EventCounters win for simplicity.
Profile first: dotnet-counters tool validates your setup externally. And yeah, it scales to multi-core beasts without breaking a sweat.
Sources
- Event Counters — .NET diagnostics for CPU usage and disk space monitoring without polling: https://learn.microsoft.com/en-us/dotnet/core/diagnostics/event-counters
- WMI Tasks: Disks and File Systems — WMI classes and queries for disk space tracking: https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-tasks--disks-and-file-systems
- Win32_VolumeChangeEvent — Event notifications for disk volume changes on Windows: https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-volumechangeevent
- WMI Process Watching Uses Too Much CPU — Discussion on WMI overhead and alternatives: https://stackoverflow.com/questions/345199/wmi-process-watching-uses-too-much-cpu-any-better-method
Conclusion
EventCounters and WMI events like Win32_VolumeChangeEvent let you monitor C# CPU usage and disk space on Windows 10 efficiently, ditching PerformanceCounters polling for good. Wrap them in listeners for 90% processor load alerts that fire only when it matters—saving cycles and sanity. Start with the code samples, profile rigorously, and scale as needed; this combo handles real-world loads without the headaches.

Use .NET EventCounters with a PollingCounter for CPU usage monitoring and disk space in C# without direct PerformanceCounters polling. Create a counter callback like PollingCounter("free-disk-space", () => DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => d.TotalFreeSpace)) for disk space, or wrap Process.TotalProcessorTime for CPU load. Counters push values at intervals (e.g., 1s); subscribe via EventListener to check thresholds like 90% CPU and trigger events, avoiding manual polling loops.
This reduces overhead compared to WMI queries but involves internal polling.

Query WMI classes like Win32_LogicalDisk for disk space and Win32_PerfFormattedData_PerfOS_Processor for CPU usage in C#, but this relies on polling. No direct event-based threshold alerts (e.g., 90% CPU) without subscriptions; use for one-off checks on Windows 10 rather than continuous monitoring to avoid high CPU from frequent WMI calls. Pair with event subscriptions for changes.

Subscribe to Win32_VolumeChangeEvent in WMI for event-driven notifications on disk additions/removals (e.g., drive letter changes) in C# on Windows 10, without polling. Properties include DriveName, EventType (e.g., Device Arrival=2), and TIME_CREATED; derived from Win32_DeviceChangeEvent. Pair with Win32_LogicalDisk queries for space thresholds, but no built-in CPU usage events or 90% processor load alerts.
WMI event watching (e.g., __InstanceCreationEvent for processes) causes high CPU usage due to inefficient polling under the hood; alternatives include Windows Management Instrumentation (WMI) optimizations or native APIs. For CPU monitoring and disk space in C#, switch to lower-overhead methods like ETW (Event Tracing for Windows) or PerformanceCounters sparingly. No direct fix for threshold events like 90% CPU without custom logic, but reduce WMI load by filtering queries narrowly.
