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
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
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.
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
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).
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);
}
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]
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
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;
}
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
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?