3

How can I get the disk usage (MB/s) of a specific process in C#?

I am able to get CPU usage and RAM usage like this:

var cpu = new PerformanceCounter("Process", "% Processor Time", ProcessName, true)
var ram = new PerformanceCounter("Process", "Working Set - Private", ProcessName, true);

Console.WriteLine($"CPU = {cpu.NextValue() / Environment.ProcessorCount} %");
Console.WriteLine($"RAM = {ram.NextValue() / 1024 / 1024} MB");

But I can't find anything related to the disk usage.

Like shown in the task manager:

et

Pedro Henrique
  • 680
  • 7
  • 22
  • See here for a list of per-process PerformanceCounters: https://msdn.microsoft.com/en-us/library/ms804621.aspx One or more of those related to I/O might be of interest for you (depending on what exactly you need/want) –  Nov 30 '18 at 15:58
  • Seems like the closest counter is "IO Data Bytes/sec", but it sums up the traffic from network, devices and "file" (disk) I/Os. The other solutions I've found so far are too overkill. I'll stick with this one. – Pedro Henrique Nov 30 '18 at 16:17

3 Answers3

0

♻️ Approach

As mentioned here^2:

This API will tell you total number of I/O operations as well as total bytes.

You can call GetProcessIoCounters to get overall disk I/O data per process - you'll need to keep track of deltas and converting to time-based rate yourself.

So, based on this C# tutorial you could do something along those lines:

struct IO_COUNTERS
{
    public ulong ReadOperationCount;
    public ulong WriteOperationCount;
    public ulong OtherOperationCount;
    public ulong ReadTransferCount;
    public ulong WriteTransferCount;
    public ulong OtherTransferCount;
}

[DllImport("kernel32.dll")]
private static extern bool GetProcessIoCounters(IntPtr ProcessHandle, out IO_COUNTERS IoCounters);

public static void Main()
{
    IO_COUNTERS counters;
    Process[] processes = Process.GetProcesses();

    foreach(Process process In processes)
    {
        try {
            GetProcessIoCounters(process.Handle, out counters);
            console.WriteLine("\"" + process.ProcessName + " \"" + " process has read " + counters.ReadTransferCount.ToString("N0") + "bytes of data.");
        } catch (System.ComponentModel.Win32Exception ex) {
        }
    }
    console.ReadKey();
}

Use this to convert it to VB.NET

But (System.ComponentModel.Win32Exception ex) occurs

System.ComponentModel.Win32Exception (0x80004005): Access is denied
   at System.Diagnostics.ProcessManager.OpenProcess(Int32 processId, Int32 access, Boolean throwIfExited)
   at System.Diagnostics.Process.GetProcessHandle(Int32 access, Boolean throwIfExited)
   at System.Diagnostics.Process.OpenProcessHandle(Int32 access)
   at System.Diagnostics.Process.get_Handle()
   at taskviewerdisktest.Form1.Main() in C:\...\source\repos\taskviewerdisktest\taskviewerdisktest\Form1.vb:line 32

Some processes seem to be really hard to access.. good news is that not many of them tho in my case (15 out of 250 or something) ..

⚠️ Disclaimer: this is more like an aproach towards "The solution" rather than "The solution"

♻️ Other Approaches & References

Giorgos Xou
  • 1,461
  • 1
  • 13
  • 32
0

You can use GetProcessDiskUsage API to get the process IO information like number of Write and Read operations and total number of bytes read or written. The following code shows the process disk usage in MB/s like in Task Manager.

enter image description here

    struct IO_COUNTERS
    {
        public ulong ReadOperationCount;
        public ulong WriteOperationCount;
        public ulong OtherOperationCount;
        public ulong ReadTransferCount;
        public ulong WriteTransferCount;
        public ulong OtherTransferCount;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetProcessIoCounters(IntPtr ProcessHandle, out IO_COUNTERS IoCounters);

    private static double GetProcessDiskUsage(IntPtr processHandle)
    {

        IO_COUNTERS ioC1 = new IO_COUNTERS();
        GetProcessIoCounters(processHandle, out ioC1);
        double totalBytes1 = (ioC1.ReadTransferCount + ioC1.WriteTransferCount) / 1024f / 1024f;

        int time = 1;
        Thread.Sleep(time * 1000);

        IO_COUNTERS ioC2 = new IO_COUNTERS();
        GetProcessIoCounters(processHandle, out ioC2);
        double totalBytes2 = (ioC2.ReadTransferCount + ioC2.WriteTransferCount) / 1024f / 1024f;

        return (totalBytes2 - totalBytes1) / time;

    }

    static void Main(string[] args)
    {

        Console.WriteLine("Enter the process ID:");

        if (!Int32.TryParse(Console.ReadLine(), out int processId))
        {
            Console.WriteLine("Invalid process ID");
            return;
        }

        Process process = Process.GetProcessById(processId);

        while (true)
        {
            double disk = Math.Round(GetProcessDiskUsage(process.Handle), 2);

            Console.WriteLine($"{process.ProcessName}: {disk} MB/s");

        }

    }
Ahmed Osama
  • 348
  • 1
  • 9
-2

You can use DriveInfo

using System;
using System.IO;

class Info {
    public static void Main() {
        DriveInfo[] drives = DriveInfo.GetDrives();
        foreach (DriveInfo drive in drives) {
            //There are more attributes you can use.
            //Check the MSDN link for a complete example.
            Console.WriteLine(drive.Name);
            if (drive.IsReady) Console.WriteLine(drive.TotalSize);
        }
    }
}
hmiedema9
  • 948
  • 4
  • 17