Introduction
Often, it is needed to track a system's idle time and execute code if the system becomes idle for a certain period of time. Even though .NET framework does not have built-in functionality for this, it is quite easy to implement it using P/Invoke. Anyway, I wanted to develop a component that you could put on a form and set the parameters, and it will do the rest. So, here it is.
Getting system's idle time
First, a little bit explanation about how to track a system's idle time. Since the .NET framework has no built-in functionality for this, we must use the Win32 API. We can use the GetLastInputInfo
function for this purpose. The GetLastInputInfo
function is located in User32.dll.
<DllImport("User32.dll")> _
Private Shared Function GetLastInputInfo(ByRef lii As LASTINPUTINFO) As Boolean
End Function
Since GetLastInputInfo
requires a LASTINPUTINFO
structure, it must also be defined.
Public Structure LASTINPUTINFO
Public cbSize As UInteger
Public dwTime As UInteger
End Structure
And, the function that will return the total idle time is:
Public Shared Function GetIdle() As UInteger
Dim lii As New LASTINPUTINFO()
lii.cbSize = Convert.ToUInt32((Marshal.SizeOf(lii)))
GetLastInputInfo(lii)
Return Convert.ToUInt32(Environment.TickCount) - lii.dwTime
End Function
That's all the code needed to find the total idle time. All code related to Win32 API is located in the Win32Wrapper
class. Here is the complete Win32Wrapper
class.
Public Class Win32Wrapper
Public Structure LASTINPUTINFO
Public cbSize As UInteger
Public dwTime As UInteger
End Structure
<DllImport("User32.dll")> _
Private Shared Function GetLastInputInfo(ByRef lii As LASTINPUTINFO) As Boolean
End Function
Public Shared Function GetIdle() As UInteger
Dim lii As New LASTINPUTINFO()
lii.cbSize = Convert.ToUInt32((Marshal.SizeOf(lii)))
GetLastInputInfo(lii)
Return Convert.ToUInt32(Environment.TickCount) - lii.dwTime
End Function
End Class
The SystemIdleTimer component
My primary goal was to create a component that would be simple to use. The result is that the SystemIdleTimer
component has only a few properties. The component has a MaxIdleTime
property that is used to define the maximum idle time in seconds.
For example, to set MaxIdleTime
to 2 seconds, you can use the following code:
SystemIdleTimer1.MaxIdleTime = 2
The component exposes the following events:
OnEnterIdleState
- This event is fired when the system becomes idle for an amount of time defined in the MaxIdleTime
property.
OnExitIdleState
- This event is fired when the system exists the idle state.
The SystemIdleTimer
's Start
method is used to start tracking the idle time. A Stop
method is also available.
Here is the SystemIdleTimer
class code:
Public Class SystemIdleTimer _
Inherits Component
Private Const INTERNAL_TIMER_INTERVAL As Double = 550
<Description("Event that if fired when idle state is entered.")> _
Public Event OnEnterIdleState(ByVal sender As Object, ByVal e As IdleEventArgs)
<Description("Event that is fired when leaving idle state.")> _
Public Event OnExitIdleState(ByVal sender As Object, ByVal e As IdleEventArgs)
Private ticker As Timers.Timer
Private m_MaxIdleTime As Integer
Private m_LockObject As Object
Private m_IsIdle As Boolean = False
<Description("Maximum idle time in seconds.")> _
Public Property MaxIdleTime() As UInteger
Get
Return m_MaxIdleTime
End Get
Set(ByVal value As UInteger)
If value = 0 Then
Throw New ArgumentException("MaxIdleTime must be larger then 0.")
Else
m_MaxIdleTime = value
End If
End Set
End Property
Public Sub New()
m_LockObject = New Object()
ticker = New Timers.Timer(INTERNAL_TIMER_INTERVAL)
AddHandler ticker.Elapsed, AddressOf InternalTickerElapsed
End Sub
Public Sub Start()
ticker.Start()
End Sub
Public Sub [Stop]()
ticker.Stop()
SyncLock m_LockObject
m_IsIdle = False
End SyncLock
End Sub
Public ReadOnly Property IsRunning() As Boolean
Get
Return ticker.Enabled
End Get
End Property
Private Sub InternalTickerElapsed(ByVal sender As Object, _
ByVal e As Timers.ElapsedEventArgs)
Dim idleTime As UInteger = Win32Wrapper.GetIdle()
If idleTime > (MaxIdleTime * 1000) Then
If m_IsIdle = False Then
SyncLock m_LockObject
m_IsIdle = True
End SyncLock
Dim args As New IdleEventArgs(e.SignalTime)
RaiseEvent OnEnterIdleState(Me, args)
End If
Else
If m_IsIdle Then
SyncLock m_LockObject
m_IsIdle = False
End SyncLock
Dim args As New IdleEventArgs(e.SignalTime)
RaiseEvent OnExitIdleState(Me, args)
End If
End If
End Sub
End Class
The class IdleEventArgs
is also needed for firing the OnEnterIdleState
and OnExitIdleState
events.
Public Class IdleEventArgs_
Inherits EventArgs
Private m_EventTime As DateTime
Public ReadOnly Property EventTime() As DateTime
Get
Return m_EventTime
End Get
End Property
Public Sub New(ByVal timeOfEvent As DateTime)
m_EventTime = timeOfEvent
End Sub
End Class
Using the SystemIdleTimer component
All you have to do is to put the SystemIdleTimer
component on your form, then define the MaxIdleTime
property of the SystemIdleTimer
component.
Then, define the code needed to execute on the OnEnterIdleState
and OnExitIdleState
events.
And somewhere in your code, start the SystemIdleTimer
component using its Start
method. For example:
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
SystemIdleTimer1.Start()
End Sub
That's it. I hope you will find this article useful.