1

Im currently messing around with a timer and a label and a performance counter because I am trying to make a label update its content to what ever the CPU usage is at the moment (and keeps updating at a certain interval)

But for some reason it just shows that my CPU usage is at 0% and its not updating.

What is causing this bug.

public void timerControl()
{

    DispatcherTimer dtClockTime = new DispatcherTimer();

    dtClockTime.Interval = new TimeSpan(0, 0, 1); //in Hour, Minutes, Second.
    dtClockTime.Tick += dtClockTime_Tick;
    dtClockTime.IsEnabled = true;
    dtClockTime.Start();

}

private void dtClockTime_Tick(object sender, EventArgs e)
{
    getCPUInfo();
}

public void getCPUInfo()
{

    PerformanceCounter cpuCounter;
    cpuCounter = new PerformanceCounter();
    cpuCounter.CategoryName = "Processor";
    cpuCounter.CounterName = "% Processor Time";
    cpuCounter.InstanceName = "_Total";
    // Get Current Cpu Usage
    string currentCpuUsage =
    cpuCounter.NextValue() + "%";
    //Print it to the current label
    CPUUsage.Content = currentCpuUsage;
}

XAML

<Page x:Class="Hawk_PC.systemPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:Hawk_PC"
      mc:Ignorable="d" 
      Background="#03a3d2"
      d:DesignHeight="350" d:DesignWidth="525"
      Title="systemPage">


    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <Label x:Name="osInfoLabel" Content="osInfoFriendlyName" HorizontalAlignment="Left" Margin="-206,-76,0,0" VerticalAlignment="Top"/>
        <Image x:Name="image" HorizontalAlignment="Left" Height="83" Margin="-258,94,0,-119" VerticalAlignment="Top" Width="100" Source="Images/Logo.png"/>
        <Label x:Name="osInfo" Content="OS:" HorizontalAlignment="Left" Margin="-234,-76,0,0" VerticalAlignment="Top"/>
        <Label x:Name="ramInfo" Content="RAM:" HorizontalAlignment="Left" Margin="-234,-50,0,0" VerticalAlignment="Top"/>
        <Label x:Name="hddInfo" Content="HDD Storage:" HorizontalAlignment="Left" Margin="-234,2,0,0" VerticalAlignment="Top"/>
        <Label x:Name="systemHealth" Content="System Diagnose:" HorizontalAlignment="Left" Margin="-234,28,0,0" VerticalAlignment="Top"/>
        <Label x:Name="cpuInfo" Content="CPU:" HorizontalAlignment="Left" Margin="-234,-24,0,0" VerticalAlignment="Top"/>
        <Label x:Name="CPUUsage" Content="Label" HorizontalAlignment="Left" Margin="-190,-24,0,0" VerticalAlignment="Top"/>
        <Button x:Name="startScanButton" Click="startScanButton_Click" Background="Transparent" BorderThickness="0" Content="Start Diagnose" HorizontalAlignment="Left" Margin="-258,55,0,-10" VerticalAlignment="Top" Width="137" Height="31"/>


    </Grid>
</Page>
B.K.
  • 9,982
  • 10
  • 73
  • 105
  • 1- Are you raising the event "property changed" in the setter of your properties ? 2- Is it really necessary to create a new object each time the timer is elapsed ? 3- Do you have a View Model ? are you using MVVM ? 4- Can you show us your view (Xaml code) ? – NTinkicht Aug 26 '16 at 09:04
  • @NTinkicht He's not using binding, so INotifyPropertyChanged interface is not applicable. He's setting the label's content in getCPUInfo(). – B.K. Aug 26 '16 at 09:12
  • Can you show us your Xaml code then please ? – NTinkicht Aug 26 '16 at 09:14
  • John, where do you call timerControl() from? Have you stepped through the code to see that the getCPUInfo code is actually executing and setting the label's content? – B.K. Aug 26 '16 at 09:17
  • I'l add the XAML. And yes it does set the label's content but it jsut sets it to 0. – John M. Henreick Aug 26 '16 at 09:34
  • @JohnM.Henreick I think I found the fix. Check out my answer. First part addresses your current approach and the second part presents a better way. – B.K. Aug 26 '16 at 09:36

2 Answers2

2

It appears that the method you're using might need a second call to NextValue() and a pause between two calls to get it to work, based on https://blogs.msdn.microsoft.com/bclteam/2006/06/02/how-to-read-performance-counters-ryan-byington/ and comments in this SO post: How to get the CPU Usage in C#?

So, you'd modify your getCPUInfo() method to look something like this:

public void getCPUInfo()
{

    PerformanceCounter cpuCounter;
    cpuCounter = new PerformanceCounter();
    cpuCounter.CategoryName = "Processor";
    cpuCounter.CounterName = "% Processor Time";
    cpuCounter.InstanceName = "_Total";
    // Get Current Cpu Usage
    cpuCounter.NextValue();
    // Sleep
    Thread.Sleep(1000);
    // Get Current Cpu Usage again
    string currentCpuUsage = cpuCounter.NextValue() + "%";
    //Print it to the current label
    CPUUsage.Content = currentCpuUsage;
}

Obviously, I hope that you understand that using Thread.Sleep() in a UI-centric program is a bad thing, so I'd use tasks and delays if you were to go with this approach (I just don't know how familiar you are with TPL, so I didn't want to bring in any further confusion).

Here's the demo of it working:

enter image description here

With that being said, you can solve everything by simply doing something like this:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;

namespace TestApp
{
    public partial class MainWindow : Window
    {
        PerformanceCounter cpuCounter;
        DispatcherTimer dtClockTime;

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            InitializeCpuPerformanceCounter();
            InitializeDispatcherTimer();
        }

        void InitializeDispatcherTimer()
        {
            dtClockTime = new DispatcherTimer();
            dtClockTime.Interval = new TimeSpan(0, 0, 1); //in Hour, Minutes, Second.
            dtClockTime.Tick += dtClockTime_Tick;
            dtClockTime.IsEnabled = true;
            dtClockTime.Start();
        }

        void InitializeCpuPerformanceCounter()
        {
            cpuCounter = new PerformanceCounter();
            cpuCounter.CategoryName = "Processor";
            cpuCounter.CounterName = "% Processor Time";
            cpuCounter.InstanceName = "_Total";
        }

        private void dtClockTime_Tick(object sender, EventArgs e)
        {
            getCPUInfo();
        }

        public void getCPUInfo()
        {
            CPUUsage.Content = cpuCounter.NextValue() + "%";
        }
    }
}

Here's a demo:

enter image description here

It's working due to the fact that I am not instantiating a new performance counter on every tick and since the timer has a one second interval, that causes the needed delay between each NextValue() call. I store both the PerformanceCounter and the DispatcherTimer as fields and initialize only once. So, this is a much better approach.

By the way, the XAML code for both tests is incredibly simple:

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="525">
    <Grid>
        <Label x:Name="CPUUsage" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

EDIT:

Per your request in the comments, here's something that would make use of async and await if you were to stay with the original approach. This will prevent the UI from freezing up for one second every other second, as it would do with the Thread.Sleep() version:

public async Task getCPUInfo()
{
    PerformanceCounter cpuCounter;
    cpuCounter = new PerformanceCounter();
    cpuCounter.CategoryName = "Processor";
    cpuCounter.CounterName = "% Processor Time";
    cpuCounter.InstanceName = "_Total";
    // Get Current Cpu Usage
    cpuCounter.NextValue();
    // Delay
    await Task.Delay(1000);
    // Get Current Cpu Usage again
    string currentCpuUsage = cpuCounter.NextValue() + "%";
    //Print it to the current label
    CPUUsage.Content = currentCpuUsage;
}

You may find more information on TPL on MSDN:

https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

Additionally, I can't recommend Stephen Cleary's blog enough. It's a gem. Here's an article on async and await:

http://blog.stephencleary.com/2012/02/async-and-await.html

He has a lot of other articles as well and his book is top notch (although, if you can't afford it, all the content in that book is throughout his blog -- he's a good guy).

Community
  • 1
  • 1
B.K.
  • 9,982
  • 10
  • 73
  • 105
  • @JohnM.Henreick Glad I could help. – B.K. Aug 26 '16 at 09:41
  • Wow your answer is amazing and very helpful! And I am not very familiar with TPL. Might need to check into that because I've heard that Thread.Sleep() can be bad for the application due to a high ammount of CPU usage and other things. Would you mind giving me an example of TPL or a good source of information? And again thanks a bunch! – John M. Henreick Aug 26 '16 at 09:41
  • And would threading be a good solution to this? Because it freezes the application in between intervals, was thinking that I might be able to create a thread that goes on behind the scenes. – John M. Henreick Aug 26 '16 at 09:44
  • @JohnM.Henreick Take a look at my edit towards the bottom of my answer. I've provided a solution that will avoid freez-ups and put you onto a path to learn TPL. However, like I said, my modified approach doesn't require any delay and double calls to the counter to get the value (and is actually a better practice, since you're not creating a new counter every two seconds). – B.K. Aug 26 '16 at 09:51
  • 1
    Oh alright! Again thanks for the amazing information! Stack Overflow Member Of The Year! – John M. Henreick Aug 26 '16 at 09:58
1

Replace the code in getCPUInfo() method with the below code,

    /// <summary>
    /// 
    /// </summary>
    public void getCPUInfo()
    {
        PerformanceCounter cpuCounter;
        cpuCounter = new PerformanceCounter();
        cpuCounter.CategoryName = "Processor";
        cpuCounter.CounterName = "% Processor Time";
        cpuCounter.InstanceName = "_Total";
        // Get Current Cpu Usage

        cpuCounter.NextValue(); // Added
        System.Threading.Thread.Sleep(1000); // Added
        // now matches task manager reading 
        float secondValue = cpuCounter.NextValue(); // Added

        string currentCpuUsage = secondValue + "%";
        //Print it to the current label
        CPUUsage.Content = currentCpuUsage;
    }

And hope your problem will be resolved.

G K
  • 2,481
  • 4
  • 29
  • 45
  • Considering `NextValue()` returns a float, why would you use `dynamic`? What's more, why would you even try to capture the first value in `firstValue` if you're never going to use it. – B.K. Aug 26 '16 at 17:56
  • @B.K. Yes, you are not required to capture firstvalue and yes, of course if you know the value will be a float then dynamic does not matters. I have given a generic code base. But I am changing it now. – G K Aug 27 '16 at 05:40