(Print this page)

How to detect idle time in a .Net application
Published date: Thursday, March 18, 2010
On: Moer and Éric Moreau's web site

Many applications make a good use of the processing power while the user is not directly interacting with the computer by using the keyboard and/or the mouse. These applications detect what we call “idle time”. These time slots can be used to do anything that is not required in the next second but has to be done in the next hour for example.

For example, you might want to query back the database to refresh some fields on the screen. You might also want to scan a folder for some pending files to process.

I am not talking about multithreading here even though this method could be mixed with some threading mechanism to bring this whole concept to another level.

I am neither detecting activity that could happen on the PC at the same time by another application.

Demo code

Some readers will be happy to find both VB and C# code for this month demo application.

The demo application is not doing anything useful but showing you how to detect idle time. It is a Windows forms application with a single form and a small class. On the form there is a label to show the current state and a progress bar to simulate some kind of activity done while the user is inactive.

Figure 1: The demo application in action

The form also has a Timer control. In the Tick event, a method is called to calculate the current idle time. In my demo, the interval property is set to 100 milliseconds so that you can quickly see the GUI change but in a real application, you should bump up this value to a larger number like 10000 (10 seconds) or more. The Tick event is also responsible of refreshing the status label (text and color) so that you can see the current state. And if the idle time is greater than 5 seconds (again 5 seconds is only for the demo), it runs the DoSomething method.

The Tick event code is the following:

'Calculates for how long we have been idle
Dim inactiveTime = _inactiveTimeRetriever.GetInactiveTime

If (inactiveTime Is Nothing) Then
    'Unknow state
    IndicatorLabel.Text = "Unknown"
    IndicatorLabel.BackColor = Color.Yellow

ElseIf (inactiveTime.Value.TotalSeconds > 5) Then
    'Idle
    IndicatorLabel.Text = String.Format("Inactive for {0}s", _
                                        inactiveTime.Value.TotalSeconds.ToString("#"))
    IndicatorLabel.BackColor = Color.Red
    DoSomething()

Else
    'Active
    IndicatorLabel.Text = "Active"
    IndicatorLabel.BackColor = Color.Green
End If

The DoSomething method

This method gets called each time the Tick event is triggered and it is considered to be in “idle time” (no activity for more than 5 seconds). I hope you understand that the code you will write in that method shouldn’t take hours to complete. It is better to do one short thing and let the Tick event detect if you are still in idle time and call you back.

In my demo application, only the progress bar gets updated to show that something is done. The code is the following:

Private Sub DoSomething()
    With ProgressBar1
        If .Value >= .Maximum Then .Value = 0
        .Value += 1
    End With
End Sub

The Stool class

The only thing left is the idle time detection itself. For this purpose, we a have a small class called cIdleTimeStool containing this code:

Option Strict On

Imports System.Runtime.InteropServices

Public Class cIdleTimeStool

    Public Structure LASTINPUTINFO
        Public cbSize As UInteger
        Public dwTime As UInteger
    End Structure

    'API call that let us detect any keyboard and/or mouse activity
     _
    Private Shared Function GetLastInputInfo(ByRef plii As LASTINPUTINFO) As Boolean
    End Function

    'Returns the time since last user activity
    Public Function GetInactiveTime() As Nullable(Of TimeSpan)
        Dim info As LASTINPUTINFO = New LASTINPUTINFO
        info.cbSize = CUInt(Marshal.SizeOf(info))
        If (GetLastInputInfo(info)) Then
            Return TimeSpan.FromMilliseconds(Environment.TickCount - info.dwTime)
        Else
            Return Nothing
        End If
    End Function

End Class

Every time the GetInactiveTime method is called (from the Tick event of the form), a call to the GetLastInputInfo method from the Windows API. The information returned by this method is used to calculate a time span since the last time the user touched the keyboard or the mouse.

Running the application

When you launch the application, the big label at the top of the form will appear in green with an “Active” caption. If you don’t touch your mouse and keyboard for 5 seconds (as set in the Tick event), the label will turn red showing an “Inactive for x s” (the x being replaced each second) and the progress bar will also have its value updated in the same period. If you move your mouse cursor, the big label will turn green and the progress bar will pause. After another 5 seconds of inactivity, the label will turn red again and the progress bar will continue to increment.

Conclusion

It is so simple to implement in any applications but be aware that this mechanism does not fit all problems. Sometimes, multithreading is the way to go.


(Print this page)