Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Detecting Application Idleness

0.00/5 (No votes)
11 Apr 2006 2  
A utility class that alerts your code when the application is idle.

Sample Image - screenshot.jpg

Introduction

If you've ever written a GUI (WinForms) application and wanted to periodically run a background process (such as poll a database), or need to log off the user or close a connection after a certain amount of inactivity, this class may be for you.

Background

I created this class so my application could occasionally check the database for updated records (by other users). I didn't want to run the check while the user was busy entering data, and I certainly didn't want to hit the database in the middle of some transactional save and risk blocking or even deadlocking the operation. So I needed to know when both the user and the CPU were idle. I also wanted to know when the application became idle, and also wanted periodic updates while the application remained idle.

I wound up using a two-step detection process:

  1. Hook into the System.Windows.Forms.Application.Idle event to help detect when the user is idle. Note, I said help ....
  2. If the user is idle, use System.Diagnostics.Process.GetCurrentProcess().TotalProcessorTime to determine if the CPU is idle (at least for this process).

I also had to add a third component, a timer, in order to continue detecting idle state. I found that while an application has focus, it seems to get a "pulse" (in the form of an Application.Idle event) once a second, even if there are no timers in the application. However, once a different application gets focus, the background app no longer gets that pulse. If I dig around, I would expect to find that Windows� is sending a system clock update to whichever application has focus. Regardless of its origin, I needed to give the class its own "heartbeat" so it would continue to operate as a background application.

Using the code

The main code is in the static class ApplicationIdleTimer.

public static event ApplicationIdle;

This static event is what you would normally use to listen for idleness. When the application is determined to be idle, you will receive an event containing an ApplicationIdleEventArgs which tells you:

  • DateTime IdleSince: the time the application became idle.
  • TimeSpan IdleDuration: the length of time the application has been idle.

Additionally, you can query the following static class properties to learn:

  • bool IsIdle : Returns the last determined idle state. Idle state is recomputed once per second. Both the GUI and the CPU must be idle for this property to be true.
  • double CurrentGUIActivity: Returns an "indication" of the GUI activity, expressed as activity per second. 0 indicates no activity. GUI activities include user interactions (typing, moving mouse) as well as events, paint operations, etc.
  • double CurrentCPUUsage: Returns the percent CPU use for the current process (0.0-1.0). Will return double.NaN if indeterminate.

Additionally, there are a couple of adjustable settings you can use to tune the idle detection for your application:

  • double GUIActivityThreshold: The threshold (GUI activity) for determining idleness. GUI activity below this level is considered "idle".
  • double CPUUsageThreshold: The threshold (CPU usage) for determining idleness. CPU usage below this level is considered "idle". A value >= 1.0 will disable CPU idle checking.

The demo app, shown above, displays all the available properties, and allows you to adjust behavior at run-time. Run the application and simply move the mouse, and the status indicator should flip from "idle" to "busy". Increase the GUI threshold until normal mouse movement does not reset the state. Then click the "Do iterations" button to perform a CPU-intensive task, which will also flip the state to "busy". You can adjust the CPU threshold as well, and a threshold of 100% will disable CPU checks in the idle detection. The "yield" checkbox determines if the form will continue to process GUI messages while the iterations are running.

If you watch closely, you may notice that the GUI indicator sometimes exceeds its threshold, but the class continues to think the application is idle! This is because the activity may occasionally spike, but the threshold is compared to average activity per second. So as long as the average activity over a second stays below the threshold, the application is deemed idle. The CPU counter works generally the same way, except its duration can vary (based on how frequently -- or infrequently -- it is called).

Points of Interest

In reality, this demo is not the best demonstration, because all the timers and frequent painting tend to throw off the idle detection. The act of showing you how idle the application is keeps it fairly busy!

Watch the App.Idle events counter in the demo. It displays the total number of System.Windows.Forms.Application.Idle events received. You may want to compare it to the display from the following extremely trimmed down code:

using System;
using System.Windows.Forms;

namespace Demo
{
    public class IdleForm : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Label label1;
        private long idleCounter = 0;

        public IdleForm()
        {
            InitializeComponent();
            Application.Idle += 
               new System.EventHandler(this.Idle_Count);
        }
        private void InitializeComponent()
        {
            this.Size = new System.Drawing.Size(300,300);
            this.label1 = new System.Windows.Forms.Label();
            this.label1.Font = 
                new System.Drawing.Font("Microsoft Sans Serif", 18F, 
                System.Drawing.FontStyle.Regular, 
                System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
            this.label1.Location = new System.Drawing.Point(0, 0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(300,300);
            this.Controls.Add(label1);
        }

        public static void Main(string[] args) 
        {
            Application.Run(new IdleForm());
        }
        
        private void Idle_Count(object sender, System.EventArgs e)
        {
            idleCounter ++;
            label1.Text = idleCounter.ToString();
        }
    }
}

Areas of Improvement

  • Because "idleness" depends to a large degree on the capacity and speed of the machine the application runs on, one improvement would be to let the threshold values "auto-tune" by collecting usage stats for a few minutes, and adjusting the values accordingly.
  • A third idle check could be added if you wanted to make sure the computer, and not just the application, is idle. In 1.1, you simply need to monitor Process.GetProcessById(0).TotalProcessorTime, which tells you the amount of CPU time spent on the System Idle Process. In 2.0, I've heard it's a bit more difficult since the Framework no longer allows access to process 0.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here