Why calling NextValue() on PerformanceCounter object throw error? - c#

Why the following code throws the error Instance 'taskmgr' does not exist in the specified Category. when I don't pass params in ctor
var cpuCounter = new PerformanceCounter();
cpuCounter.CategoryName = "Processor";
cpuCounter.CounterName = "% Processor Time";
cpuCounter.InstanceName = "taskmgr";
cpuCounter.NextValue();
But, when I do the same by passing params in ctor no error is raised.
var cpuCounter = new PerformanceCounter(
"Processor",
"% Processor Time",
"taskmgr");
cpuCounter.NextValue();
UPDATED:
I've tried on every process name, not only "taskmgr" and the result is the same!
What is the problem?

There is no taskmgr instance available for the Processor category because Processor is about your CPUs...
You probably meant Process, which works as expected:
var cpuCounter = new PerformanceCounter();
cpuCounter.CategoryName = "Process";
cpuCounter.CounterName = "% Processor Time";
cpuCounter.InstanceName = "taskmgr";
cpuCounter.NextValue();

Are you sure you are providing the correct value for CategoryName/InstanceName? From the documentation for InstanceName it looks like instance name is supposed to match up with the values available through the Performance Monitor MMC snap-in, which only provides _Total, and an integer index for a processor on my machine, at least when providing "Processor".
If you provide "Process" for CategoryName it allows looking at a process.

Related

trouble displaying system diagnostic information in C# console app

I'm trying to display some system diagnostic information in a console app so once I know it is displayed I can send it SMTP email.
when I call this all it displays is
system.diagnoistics.performancecounter
system.diagnoistics.performancecounter
public static void GetUsageInformation()
{
cpu = new PerformanceCounter();
cpu.CategoryName = "Processor";
cpu.CounterName = "% Processor Time";
cpu.InstanceName = "_Total";
ram = new PerformanceCounter("Memory", "Available Mbytes");
Console.WriteLine(cpu);
Console.WriteLine(ram);
}
Can you provide some assistance with that I'm doing incorrectly here? I'm sure it's super simple like everything else I've run into the last few days.
What is happening here is Console.WriteLine is displaying the string representation of your PerformanceCounter objects, obtained by Console.WriteLine() calling ctr.ToString() internally, which is indeed System.Diagnostics.PerformanceCounter. What I believe you want is the string representation of the Properties of your PerformanceCounter classes.
You can either WriteLine the properties directly, ala...
Console.WriteLine(cpu.CategoryName);
Console.WriteLine(cpu.CounterName);
// etc...
Or use reflection. This will get you started...
PropertyInfo[] properties = ctr.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
Console.Write(property.Name + ":\t");
Console.WriteLine(property.GetValue(ctr).ToString());
}

How to get the current CPU/RAM/Disk usage in a C# web application using .NET CORE?

I am currently looking for a way to get the current CPU/RAM/Disk usage in a C# web application using .NET CORE.
For CPU and ram usage, I use PerformanceCounter Class from System.Diagnostics.
This is the codes:
PerformanceCounter cpuCounter;
PerformanceCounter ramCounter;
cpuCounter = new PerformanceCounter();
cpuCounter.CategoryName = "Processor";
cpuCounter.CounterName = "% Processor Time";
cpuCounter.InstanceName = "_Total";
ramCounter = new PerformanceCounter("Memory", "Available MBytes");
public string getCurrentCpuUsage(){
cpuCounter.NextValue()+"%";
}
public string getAvailableRAM(){
ramCounter.NextValue()+"MB";
}
For disk usage, I am using the DriveInfo class. This is the codes:
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);
}
}
}
Unfortunately .NET Core does not support the DriveInfo and PerformanceCounter classes, hence the code above do not work.
Does anyone know how I can get the current CPU/RAM/Disk usage in a C# web application using .NET CORE?
You can use PerformnceCounter in the System.Diagnostics.PerformanceCounter package
for example, the next code will give you the total processor usage percent
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total",true);
var value = cpuCounter.NextValue();
//Note: In most cases you need to call .NextValue() twice to be able to get the real value
if (Math.Abs(value) <= 0.00)
value = cpuCounter.NextValue();
Console.WriteLine(value);
you can do the same for all OS registered Performance Counters.
Update:
I'm not sure if there is something I should do after creating a new instance of the PerformanceCounter class, but sometimes when I get the next value it comes as 0.
So I've decided to make one instance of PerformanceCounter in at the application level.
e.g.
public static class DiagnosticHelpers
{
static float _systemCPU;
public static float SystemCPU
{
get
{
lock (locker)
{
return _systemCPU;
}
}
}
private static readonly object locker = new object();
static DiagnosticHelpers()
{
SystemCPU = 0;
Task.Run(() =>
{
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
cpuCounter.NextValue(); //prime the counter
while (true)
{
Thread.Sleep(1000); /wait at least 1 second before the first real read
lock (locker)
{
_systemCPU = cpuCounter.NextValue();
}
}
});
}
}
Processor information is available via System.Diagnostics:
var proc = Process.GetCurrentProcess();
var mem = proc.WorkingSet64;
var cpu = proc.TotalProcessorTime;
Console.WriteLine("My process used working set {0:n3} K of working set and CPU {1:n} msec",
mem / 1024.0, cpu.TotalMilliseconds);
DriveInfo is available for Core by adding the System.IO.FileSystem.DriveInfo package
For Windows i'm using this
var memorieLines= GetWmicOutput("OS get FreePhysicalMemory,TotalVisibleMemorySize /Value").Split("\n");
var freeMemory= memorielines[0].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
var totalMemory = memorielines[1].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
var cpuLines = GetWmicOutput("CPU get Name,LoadPercentage /Value").Split("\n");
var CpuUse = cpuLines[0].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
var CpuName = cpuLines[1].Split("=", StringSplitOptions.RemoveEmptyEntries)[1];
private string GetWmicOutput(string query, bool redirectStandardOutput = true)
{
var info = new ProcessStartInfo("wmic");
info.Arguments = query;
info.RedirectStandardOutput = redirectStandardOutput;
var output = string.Empty;
using (var process = Process.Start(info))
{
output = process.StandardOutput.ReadToEnd();
}
return output.Trim();
}
For the disk infos you can use this query :
LOGICALDISK get Caption,DeviceID,FileSystem,FreeSpace,Size /Value
if you want a better output formatting give a look to this article : https://www.petri.com/command-line-wmi-part-3
Add this nuget package to your project by double clicking project.
<ItemGroup>
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="6.0.0" />
</ItemGroup>
When you run the code, you will get an error like below.
Performance counters cannot be initialized! System.UnauthorizedAccessException: Access to the registry key 'Global' is denied.
To solve this error, you have to add your application pool user to "Performance Monitor Users" group.
Open command line in administrator mode, then run this command.
net localgroup "Performance Monitor Users" "IIS APPPOOL\MYAPPPOOL" /add
MYAPPPOOL will be replaced with your real app pool name.
Then restart the machine if iis restart does not solve.

C# Instance Name to Use For Network Interface Performance Counter

I'm trying to add some system monitoring functions to a WPF app I am working on and I would like to add a bandwidth use monitor. Right now I have a CPU and a RAM counter working but I can't figure out what to use for the Performance Counter instance name (sorry if that isn't the correct term). This is what I'm using so far:
PerformanceCounter cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
PerformanceCounter ramCounter = new PerformanceCounter("Memory", "Available MBytes", string.Empty);
PerformanceCounter netSentCounter = new PerformanceCounter("Network Interface", "Bytes Received/sec", );
PerformanceCounter netRecCounter = new PerformanceCounter("Network Interface", "Bytes Sent/sec", );
I have strings that update every 1 second using a timer tick method that updates my WPF labels and the RAM and the CPU counters work but I don't know what to put for the instance names for the last two network interfaces.
Thanks in advance.
Here is some sample code how to iterate through the Network Interfaces;
PerformanceCounterCategory category = new PerformanceCounterCategory("Network Interface");
String[] instancename = category.GetInstanceNames();
foreach (string name in instancename)
{
Console.WriteLine(name);
}
By utilizing the PerformanceCounterCategory, you can now obtain the Instance Names, and then use in your PerformanceCounter(s) using GetInstanceNames.

How to make a CPU Usage Control in WPF

I want to make a control which is very similar to the one used in Windows Task Manager to show the CPU Performance something like a changing vertical Guage
You can get the CPU usage values via ManagementObjectSearcher using WMI
To get the value, you do something like this
var info = ManagementObjectSearcher(#"\\localhost\root\CIMV2","SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name=\"_Total\"");
ManagementObject queryObj = info.Get().Cast<ManagementObject>().First();
var cpuUsage = Convert.ToInt32(queryObj["PercentIdleTime"]);
Alternatively you can use the System.Diagnostics PerformanceCounter instead
PerformanceCounter cpu = new PerformanceCounter(){
CategoryName = "Processor",
CounterName = "% Processor Time",
InstanceName = "_Total"
};
var yourCpuUsageValue = cpu.NextValue();
Got it...used a Progress Bar to show the values and used PerformanceCounter to get the cpu usage values.
Thanks

Exception: Instance 'Name of instance' does not exist in the specified Category

When I create and use performance counters like this:
private readonly PerformanceCounter _cpuPerformanceCounter;
public ProcessViewModel(Process process)
{
_cpuPerformanceCounter = new PerformanceCounter("Process", "% Processor Time", process.ProcessName, true);
}
public void Update()
{
CPU = (int)_cpuPerformanceCounter.NextValue() / Environment.ProcessorCount; // Exception
}
... I get an exception Instance 'Name of instance' does not exist in the specified Category and don't understand why.
P.S. Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.net>
<settings>
<performanceCounters enabled="true"/>
</settings>
</system.net>
</configuration>
... included in App.config.
Adding on to previous posts, I have seen processes being formatted like <ProcessName>_<ProcessId> - depending on the OS you are running your app on (Win XP, Win Vista, Win 7, Win 2003 or 2008 Server). In order to have a reliable way to identify your process name for obtaining other performance counters down the road, a function could look like this:
private string ObtainProcessName()
{
string baseProcessName;
string processName = null;
int processId;
bool notFound = true;
int processOptionsChecked = 0;
int maxNrOfParallelProcesses = 3 + 1;
try
{
baseProcessName = Process.GetCurrentProcess().ProcessName;
}
catch (Exception exception)
{
return null;
}
try
{
processId = Process.GetCurrentProcess().Id;
}
catch (Exception exception)
{
return null;
}
while (notFound)
{
processName = baseProcessName;
if (processOptionsChecked > maxNrOfParallelProcesses)
{
break;
}
if (1 == processOptionsChecked)
{
processName = string.Format("{0}_{1}", baseProcessName, processId);
}
else if (processOptionsChecked > 1)
{
processName = string.Format("{0}#{1}", baseProcessName, processOptionsChecked - 1);
}
try
{
PerformanceCounter counter = new PerformanceCounter("Process", "ID Process", processName);
if (processId == (int)counter.NextValue())
{
notFound = !true;
}
}
catch (Exception)
{
}
processOptionsChecked++;
}
return processName;
}
I think your issue happens when there are more than one process with the same name. What PerfMon does then is append #1, #2, etc to the process name. So that means MyApp.exe executed twice will cause this exception when you try to read the performance monitor for "MyApp". Here's a link to one way of solving this: Read performance counters by pid
Here is my solution for all processes and multiple process instances:
var processes = Process.GetProcesses().GroupBy(g => g.ProcessName);
List<Tuple<string, PerformanceCounter>> pcList = new List<Tuple<string, PerformanceCounter>>();
foreach (var pg in processes)
{
if (pg.First().ProcessName == "Idle")
continue;
if (pg.Count() == 1)
{
var process_cpu = new PerformanceCounter(
"Process",
"% Processor Time",
pg.First().ProcessName
);
process_cpu.NextValue();
pcList.Add(new Tuple<string, PerformanceCounter>(pg.First().ProcessName, process_cpu));
}
else
{
int id = 1;
foreach(var p in pg)
{
var process_cpu = new PerformanceCounter(
"Process",
"% Processor Time",
p.ProcessName + "#" + id
);
process_cpu.NextValue();
pcList.Add(new Tuple<string, PerformanceCounter>(p.ProcessName + "#" + id, process_cpu));
id++;
}
}
}
The origninal format that used a pid suffix (registry ProcessNameFormat = 1) appears to have changed as of .NET 4.5 (msdn link) to "processame_pid_rid". Thus, the accepted answer as currently written may no longer work for that case.
This solution should still work for the newer formats:
https://weblog.west-wind.com/posts/2014/Sep/27/Capturing-Performance-Counter-Data-for-a-Process-by-Process-Id
However all these matching solutions may be prone to a race condition where the instance name changes due to a process exiting (#9 becomes #8) just after the instance name was determined but before the new PerformanceCounter() was allocated.
It would make much more sense for MS to provide a PerformanceCounter constructor that accepts a Pid (and possibly now RuntimeId?) directly since the instance names can change on the fly.
You can check this code
Use > new PerformanceCounter("Processor Information", "% Processor Time", "_Total");
Instead of> new PerformanceCounter("Processor", "% Processor Time", "_Total");

Categories

Resources