Detecting idle state in WinForms apps

Its not my usual thing to post code on my blog, but so many people keep asking me for this, so here it is:

Public Class DetectActivity
   Implements IDisposable
   Implements System.Windows.Forms.IMessageFilter
 
   Private _lastActivity As Date
 
   Public Sub New()
       _lastActivity = Date.Now
       Windows.Forms.Application.AddMessageFilter(Me)
   End Sub
 
   Public ReadOnly Property LastActivity() As Date
       Get
           Return _lastActivity
       End Get
   End Property
 
   Public Function PreFilterMessage(ByRef m AsSystem.Windows.Forms.Message) As Boolean ImplementsSystem.Windows.Forms.IMessageFilter.PreFilterMessage
       Select Case m.Msg
           Case Win32Message.WM_KEYUP, Win32Message.WM_KEYDOWN
               _lastActivity = Date.Now
           Case Win32Message.WM_LBUTTONDOWN, Win32Message.WM_LBUTTONUP, _  Win32Message.WM_MBUTTONDOWN, Win32Message.WM_MBUTTONUP, _ Win32Message.WM_RBUTTONDOWN, Win32Message.WM_RBUTTONUP, _  Win32Message.WM_MOUSEMOVE, Win32Message.WM_MOUSEWHEEL
 
               _lastActivity = Date.Now
       End Select
 
       Return False
   End Function
 
   Public Sub Dispose() Implements System.IDisposable.Dispose
       Windows.Forms.Application.RemoveMessageFilter(Me)
   End Sub
 
   Enum Win32Message
       WM_KEYFIRST = &H100
       WM_KEYDOWN = &H100
       WM_KEYUP = &H101
       WM_CHAR = &H102
       WM_DEADCHAR = &H103
       WM_SYSKEYDOWN = &H104
       WM_SYSKEYUP = &H105
       WM_SYSCHAR = &H106
       WM_SYSDEADCHAR = &H107
 
       WM_MOUSEFIRST = &H200
       WM_MOUSEMOVE = &H200
       WM_LBUTTONDOWN = &H201
       WM_LBUTTONUP = &H202
       WM_LBUTTONDBLCLK = &H203
       WM_RBUTTONDOWN = &H204
       WM_RBUTTONUP = &H205
       WM_RBUTTONDBLCLK = &H206
       WM_MBUTTONDOWN = &H207
       WM_MBUTTONUP = &H208
       WM_MBUTTONDBLCLK = &H209
       WM_MOUSEWHEEL = &H20A
   End Enum
End Class

This simple class can be instantiated when a Windows Forms application starts up, and then you can ask it at any time when the last user-activity was.

This class works better than most other techniques, because:

  • it only detects activity in your own application
  • it is simple
  • it is 100% managed code
  • it works

Related Posts

  • No Related Post

10 Comments

  1. Swapnil says:

    Hello
    I was trying to refer the code given by you but since my application is in C#.net 2.0 I was getting errors while defining the Enum Can you please help me in this as I have tried a lot and since was not getting your email writing a comment for this
    Can you please give me the C# equivalent of the same if you are having it
    Please
    Thanks in advance
    Takecare Bye

  2. Steve Campbell (admin) says:

    No, I’m not re-writing it in C#!

    Probably the problem with the enum is that C# has a different form for hex values. Try 0×100 instead of &H100, etc.

  3. Swapnil says:

    Hello Admin

    Thanks for reply.
    which is the way to carried Out.
    I have followed this steps
    I have added the class file in my application.like this
    class DetectActivity : IDisposable,System.Windows.Forms.IMessageFilter
    {
    private DateTime _lastActivity;

    public void New()
    {
    _lastActivity = DateTime.Now;
    System.Windows.Forms.Application.AddMessageFilter(this);

    }
    public DateTime LastActivity
    {
    get
    {
    return _lastActivity;
    }
    }

    enum Win32Message
    {
    WM_KEYFIRST = 0X100,
    //WM_KEYDOWN = 0×0,H100,
    WM_KEYUP = 0×101,
    WM_CHAR = 0×102,
    WM_DEADCHAR = 0×103,
    WM_SYSKEYDOWN = 0×104,
    WM_SYSKEYUP = 0×105,
    WM_SYSCHAR = 0×106,
    WM_SYSDEADCHAR = 0×107,

    //WM_MOUSEFIRST = 0×0, H200,
    WM_MOUSEMOVE = 0×200,
    WM_LBUTTONDOWN = 0×201,
    WM_LBUTTONUP = 0×202,
    WM_LBUTTONDBLCLK = 0×203,
    WM_RBUTTONDOWN = 0×204,
    WM_RBUTTONUP = 0×205,
    WM_RBUTTONDBLCLK = 0×206,
    WM_MBUTTONDOWN = 0×207,
    WM_MBUTTONUP = 0×208,
    WM_MBUTTONDBLCLK = 0×209,
    WM_MOUSEWHEEL = 0×20A

    }

    #region IDisposable Members

    public void Dispose()
    {
    System.Windows.Forms.Application.RemoveMessageFilter(this);
    }

    #endregion

    #region IMessageFilter Members

    public bool PreFilterMessage(ref System.Windows.Forms.Message m)
    {
    switch(m.Msg)
    {

    case (int)Win32Message.WM_KEYUP :
    _lastActivity =DateTime.Now ;
    break;
    //case Win32Message.WH_KEYBOARD_LL:
    // _lastActivity =DateTime.Now ;
    case (int)Win32Message.WM_LBUTTONDOWN:
    _lastActivity =DateTime.Now ;
    break;
    case (int)Win32Message.WM_LBUTTONUP:
    _lastActivity = DateTime.Now;
    break;
    case (int)Win32Message.WM_MBUTTONDOWN:
    _lastActivity = DateTime.Now;
    break;
    case (int)Win32Message.WM_MBUTTONUP:
    _lastActivity = DateTime.Now;
    break;
    case (int)Win32Message.WM_RBUTTONDOWN:
    _lastActivity = DateTime.Now;
    break;
    case (int)Win32Message.WM_RBUTTONUP:
    _lastActivity = DateTime.Now;
    break;
    case (int)Win32Message.WM_MOUSEMOVE:
    _lastActivity = DateTime.Now;
    break;
    case (int)Win32Message.WM_MOUSEWHEEL:
    _lastActivity = DateTime.Now;
    break;

    }
    return false;

    }

    #endregion
    }
    Now in my main form I made instance of the class.
    And On a thread timer Callback I tried to find out the difference between the current time nad _lastactivity time But it once making the status idle after 30 which is given by me. And then doesn’t reflect even though I clicked on the form/press any key
    can you please help me in this as this could be very helpful for me
    thanks in advance
    takecare Bye :)

  4. Steve Campbell says:

    The code looks fine to me. I am not sure I understand the problem. Is the PreFilterMessage code not executing when you click or press keys on the form? (You could put a breakpoint on one of the switch cases to check).

  5. Peter Voutov says:

    The problem is with his translation into C#. A constructor in C# is not a void New() function :)

    It should look like this:

    public DetectActivity ()
    {
    _lastActive = DateTime.Now;
    Application.AddMessageFilter(this);
    }

  6. Martin Gómez says:

    Thanks!
    That’s the best approach I found to the “application idle time” problem.
    I left my C# translation for the interested party

    [code]
    public class DetectActivity : IDisposable, System.Windows.Forms.IMessageFilter
    {

    private System.DateTime _lastActivity;

    public DetectActivity()
    {
    _lastActivity = System.DateTime.Now;
    Windows.Forms.Application.AddMessageFilter(this);
    }

    public System.DateTime LastActivity {
    get { return _lastActivity; }
    }

    public bool PreFilterMessage(ref System.Windows.Forms.Message m)
    {
    switch (m.Msg) {
    case Win32Message.WM_KEYUP:
    case Win32Message.WM_KEYDOWN:
    _lastActivity = System.DateTime.Now;
    break;
    case Win32Message.WM_LBUTTONDOWN:
    case Win32Message.WM_LBUTTONUP:
    case Win32Message.WM_MBUTTONDOWN:
    case Win32Message.WM_MBUTTONUP:
    case Win32Message.WM_RBUTTONDOWN:
    case Win32Message.WM_RBUTTONUP:
    case Win32Message.WM_MOUSEMOVE:
    case Win32Message.WM_MOUSEWHEEL:
    _lastActivity = System.DateTime.Now;
    break;
    }

    return false;
    }

    public void Dispose()
    {
    Windows.Forms.Application.RemoveMessageFilter(this);
    }

    public enum Win32Message
    {
    WM_KEYFIRST = 0x100,
    WM_KEYDOWN = 0x100,
    WM_KEYUP = 0x101,
    WM_CHAR = 0x102,
    WM_DEADCHAR = 0x103,
    WM_SYSKEYDOWN = 0x104,
    WM_SYSKEYUP = 0x105,
    WM_SYSCHAR = 0x106,
    WM_SYSDEADCHAR = 0x107,

    WM_MOUSEFIRST = 0x200,
    WM_MOUSEMOVE = 0x200,
    WM_LBUTTONDOWN = 0x201,
    WM_LBUTTONUP = 0x202,
    WM_LBUTTONDBLCLK = 0x203,
    WM_RBUTTONDOWN = 0x204,
    WM_RBUTTONUP = 0x205,
    WM_RBUTTONDBLCLK = 0x206,
    WM_MBUTTONDOWN = 0x207,
    WM_MBUTTONUP = 0x208,
    WM_MBUTTONDBLCLK = 0x209,
    WM_MOUSEWHEEL = 0x20a
    }
    }
    [/code]

  7. Terry says:

    How do I implement in my MDI application in VB.NET? And I want to know how long the application was idle, if more then X mins,then exit the applicaiton. Where do I add that code? Can anybody help me. Though I am kind od new in .NET, but you may provide me solution in VB or C#, I will convert it as I know C/C++/Java/C#/VB, etc.

    Any help is great at this moment.

    Thanks

  8. Mattew says:

    Good Code, found 1 problem though it appears that the WM_MOUSEMOVE_EVENT is being sent at regular intervals to fix I added the following (in c#)

    System.Drawing.Point mousept;
    public bool PreFilterMessage(ref Message m)
    {
    switch ((Win32.Message)m.Msg)
    {
    case Win32.Message.WM_MOUSEMOVE:
    if (mousept.Equals(Cursor.Position))
    return false;
    mousept = Cursor.Position;
    UpdateActivity();
    break;
    case Win32.Message.WM_KEYUP:
    case Win32.Message.WM_KEYDOWN:
    case Win32.Message.WM_LBUTTONDOWN:
    case Win32.Message.WM_LBUTTONUP:
    case Win32.Message.WM_MBUTTONDOWN:
    case Win32.Message.WM_MBUTTONUP:
    case Win32.Message.WM_RBUTTONDOWN:
    case Win32.Message.WM_RBUTTONUP:
    case Win32.Message.WM_MOUSEWHEEL:
    UpdateActivity();
    break;
    default:
    break;
    }
    return false;
    }

  9. dr says:

    I hope this sample answer Terry question.

    Public Class Main
    Inherits System.Windows.Forms.Form

    Dim objActivity As Object = New DetectActivity

    Private Sub MyBase_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

    Timer1.Start()
    Timer1.Interval = 30 * 1000 ‘ in milisec

    End Sub

    Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    Dim diff As Integer = DateDiff(DateInterval.Second, objActivity.LastActivity, Now)
    Label1.Text = diff
    If diff > 100 Then
    End
    End If

    End Sub

    End Class

  10. Wayne says:

    Hi,

    Im currently using this code for my application. But I’ve got problem in here:

    Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    Dim diff As Integer = DateDiff(DateInterval.Second, objActivity.LastActivity, Now)
    Label1.Text = diff
    If diff > 100 Then
    End
    End If

    End Sub

    In timer tick event, the objActivity.LastActivity always return me current datetime even though i’ve been idle in the application, any help in this matter?

Leave a Reply