Solved

Application idle in VB.net

Posted on 2006-10-27
10
5,145 Views
Last Modified: 2008-01-09
Helli,
I want to detect whether my application is idle for 5 mins and then perform certain operations. Below is my code to detect mouse movement

  Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

    Private Declare Function GetQueueStatus Lib "user32" _
       (ByVal fuFlags As Long) As Long

    Private Const QS_KEY = &H1
    Private Const QS_MOUSEMOVE = &H2
    Private Const QS_MOUSEBUTTON = &H4
    Private Const QS_MOUSE = (QS_MOUSEMOVE Or QS_MOUSEBUTTON)
    Private Const QS_INPUT = (QS_MOUSE Or QS_KEY)

    Public bCancel As Boolean

    Private Sub Command1_Click()
        ' one minute idle time out
        'CheckInputIdle(1)
    End Sub

    Private Sub Form_Unload(ByVal Cancel As Integer)
        bCancel = True ' kill the polling loop if it is running
    End Sub

    Public Sub CheckInputIdle(ByVal TimeOut_InMinutes As Long)
        Dim idleTarget As Date

        idleTarget = DateAdd("n", TimeOut_InMinutes, Now())
        '   Debug.Print("idleTarget = " & idleTarget)
        Do While bCancel = False
            If GetQueueStatus(QS_INPUT) Then
                idleTarget = DateAdd("n", TimeOut_InMinutes, Now())
           
            End If

           
            Sleep(50) ' keep CPU usage down

            If Now() >= idleTarget Then
                Exit Do
            End If
        Loop

        ' optional MsgBox...
        If bCancel = False Then
            MsgBox("Process idle is for " & TimeOut_InMinutes & " minutes.")
        End If

'perform actions on idle here

    End Sub


 Public Sub AppIdle(ByVal sender As System.Object, ByVal e As System.EventArgs)

        CheckInputIdle(5)

    End Sub


However the above does not work ?

Also how can I scale to look for keyboard events as well

Please help asap

Is there a eaier way to implement the above ?
0
Comment
Question by:jbajaj
  • 4
  • 3
  • 3
10 Comments
 
LVL 9

Expert Comment

by:DjDezmond
Comment Utility
Im not sure what your actual application does, but the loop you have implemented, would stop your application from doing any useful work.

You would be better executing this idle check code on a seperate thread so your application will not freeze.

Also, the sleep function is from VB6, vb.net does not have a global sleep function. You would use: system.threading.sleep (which will sleep the current thread)

The way i would do it, for simplicity, is to declare a sub (a property or global variable might even be sufficient), and call it something like "LastJobTime". At the end of EVERY subroutine in your app, I would call "LastJobTime" which would set a property to the current time.

I would then set up a timer to constantly check this property, it should have a interval of around 30 seconds, and it checks the current time against the "LastJobTime". If 5 minutes (for example) have passed, then the application has been idle for 5 minutes.

The obvious draw back to this is the amount of sub routines your app has (the bigger it is, the longer its going to take you to put "LastJobTime" at the end of every sub!)

___________________________________________________________________

Alternatively, you can look at (explains how to intercept key and mouse events in the windows form):
  http://www.informit.com/articles/article.asp?p=31218&seqNum=2&rl=1

This explains the Application.RaiseIdle event...
  http://msdn2.microsoft.com/en-us/library/system.windows.forms.application.raiseidle.aspx

You could also set a system hook to intercept ALL low level messages being sent to your app (from keyboard, mouse etc) and make a sub round that (i think that would be the cleanest way), but it requires more processing/cpu time/power, and is generally complicated to set up.
  http://www.colinneller.com/blog/PermaLink,guid,2838f59a-f4af-4c95-a322-b9ee5918a39c.aspx
0
 
LVL 9

Accepted Solution

by:
DjDezmond earned 500 total points
Comment Utility
This may explain system hooks slightly better (one of the links in the following forum thread is actually the last link i put in my last post)
  http://www.thescripts.com/forum/thread442287.html

I should of explained this abit more also... the Application.RaiseIdle event is merely something you can call when you decide your app is in an idle state. It allows the app to do some 'underbody' work that it normally wouldn't do whilst the app is in a working state.

Let me know if you require anymore information...

Dez
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
The code you posted would work for VB6 if you had a DoEvents call in the polling loop (it has been removed for some unknown reason).  But that doesn't matter since the GetQueueStatus() approach doesn't work in VB.Net.

The low level mouse/keyboard hook approach would work but  then you would need to check if your app is the foreground window.  This can get tricky if you have multiple windows in your app.

Another approach would be to implement the IMessageFilter interface in your app and trap mouse/keyboard activity that way.
0
 

Author Comment

by:jbajaj
Comment Utility
Hello

I am a bit confised here . Sorry could be because of my lack of knowledge in this subject. Can someone point me to some code showing how to detect inactivity in a form in VB

Thanks
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Author Comment

by:jbajaj
Comment Utility
Hello

Please let me know if the below code would give me any problems

Imports System.Text
Imports System
Imports System.Data


Imports System.Windows.Forms


Public Class Form1
    Inherits System.Windows.Forms.Form
    Implements IMessageFilter '<<< Implement the interface




#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        Application.AddMessageFilter(Me)

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Timer1 As System.Windows.Forms.Timer
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container
        Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
        '
        'Timer1
        '
        Me.Timer1.Interval = 10000
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 273)
        Me.Name = "Form1"
        Me.Text = "Form1"

    End Sub

#End Region

    'This method is called by the system for all messages
    Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean _
        Implements System.Windows.Forms.IMessageFilter.PreFilterMessage

        If Not Me.IsDisposed Then
            'reset the clock here for any input you would like to test for
            Const WM_LBUTTONDOWN = &H201
            Const WM_RBUTTONDOWN = &H204
            Const WM_KEYDOWN = &H100
            Const QS_MOUSEMOVE = &H200

            Select Case m.Msg
                Case WM_LBUTTONDOWN, WM_RBUTTONDOWN, QS_MOUSEMOVE
                    resetClock("Mouse")
                Case WM_KEYDOWN
                    resetClock("KeyPress")
                Case QS_MOUSEMOVE
                    resetClock("mousemove")
            End Select


            'pass the message on to the form
            Return False
        End If

    End Function

    Private Sub resetClock(ByVal method As String)
        'you would actually reset your timer method here
        'Just output to debug for illustration
        Debug.WriteLine(method & " reset me " & Now)
        Timer1.Stop()


    End Sub

    Public Sub AppIdle(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Timer1.Enabled = True


    End Sub


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
        AddHandler Application.Idle, AddressOf AppIdle

    End Sub

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

        MessageBox.Show("Form was idle for 10 seconds")
        Timer1.Stop()

    End Sub
End Class
0
 

Author Comment

by:jbajaj
Comment Utility
Hello

The below code seems to work for me. Can anyone please let me know if this would cause any issues

Imports System.Text
Imports System
Imports System.Data


Imports System.Windows.Forms


Public Class Form1
    Inherits System.Windows.Forms.Form
    Implements IMessageFilter '<<< Implement the interface




#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        Application.AddMessageFilter(Me)

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Timer1 As System.Windows.Forms.Timer
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container
        Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
        '
        'Timer1
        '
        Me.Timer1.Interval = 10000
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 273)
        Me.Name = "Form1"
        Me.Text = "Form1"

    End Sub

#End Region

    'This method is called by the system for all messages
    Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean _
        Implements System.Windows.Forms.IMessageFilter.PreFilterMessage

        If Not Me.IsDisposed Then
            'reset the clock here for any input you would like to test for
            Const WM_LBUTTONDOWN = &H201
            Const WM_RBUTTONDOWN = &H204
            Const WM_KEYDOWN = &H100
            Const QS_MOUSEMOVE = &H200

            Select Case m.Msg
                Case WM_LBUTTONDOWN, WM_RBUTTONDOWN, QS_MOUSEMOVE
                    resetClock("Mouse")
                Case WM_KEYDOWN
                    resetClock("KeyPress")
                Case QS_MOUSEMOVE
                    resetClock("mousemove")
            End Select


            'pass the message on to the form
            Return False
        End If

    End Function

    Private Sub resetClock(ByVal method As String)
        'you would actually reset your timer method here
        'Just output to debug for illustration
        Debug.WriteLine(method & " reset me " & Now)
        Timer1.Stop()


    End Sub

    Public Sub AppIdle(ByVal sender As System.Object, ByVal e As System.EventArgs)

        Timer1.Enabled = True

    End Sub


 

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

        MessageBox.Show("Form was idle for 10 seconds")
        Timer1.Stop()

    End Sub

    Private Sub Form1_Load_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        AddHandler Application.Idle, AddressOf AppIdle
    End Sub
End Class


Thank you
0
 
LVL 9

Expert Comment

by:DjDezmond
Comment Utility
I havn't tested the code, so assuming the IMessageFilter works ok, the only issue i can see is...

When you move your mouse over the form, its going to reset the idle counter... I would argue that you can move your mouse across the form without actually using it, but this will be down to the circumstances of your problem.
0
 

Author Comment

by:jbajaj
Comment Utility
Hello

I have 2 quereis here

Query 1
======
Form the above form

  Public Sub AppIdle(ByVal sender As System.Object, ByVal e As System.EventArgs)

        Timer1.Enabled = True
        Timer1.start()

    End Sub



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

        Timer1.Stop()
        Timer1.Enabled = False
        MessageBox.Show("Your session has expired")

        Me.Dispose()' dispaose this form
        Dim member1 As New member_form ' launch other form
       
        member1.ShowDialog()
        Me.Show()


    End Sub


Now here is my doubt.
if i remain in the same form and dont press OK, no other messagebox pops up which makes me to believe that the timer has  indeed stopped

However when i press ok, according to the code above I move to member1 . Suprisingly If i remain idle for 20 seconds the timer again shows up . How I avoid this ???
Id the appidle global across the entire project. If so I wan to make it only applicable to a form. Is that a reason or has my timer not stopped

Please help on this


Query 2
Extension to the above...I wan to display a countdown on the message box so I bought in another timer2 with nterval 1000 seconds

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

        Timer1.Stop()
        Timer1.Enabled = False
        MessageBox.Show("Your session has expired")
  Dim s As Integer
        Dim s1 As String
        s = 21
        Timer2.Enabled = True
        Timer2.Start()

   
    End Sub


 Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
        s = s - 1


        Dim ans As DialogResult

        ans = MessageBox.Show("Your session will expire in" + CStr(s) + "seconds" + vbCrLf + "Click Ok to continue")

        Dim ans1 As Message

        If s = 0 Then

            Me.Dispose()
            Dim member1 As New member_form

            member1.ShowDialog()
            Me.Show()

        End If

        If ans = DialogResult.OK Then
            Timer2.Stop()
            Timer2.Enabled = False
        End If

    End Sub

However I keep getting 1 messagebox on the other . How can I update the value of s on the same messagebox.
Please help asap
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
(been awhile since I looked at this questions comments...)

Regarding your code:

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Timer1.Stop()
        Timer1.Enabled = False
        MessageBox.Show("Your session has expired")

        Me.Dispose()' dispaose this form
        Dim member1 As New member_form ' launch other form
       
        member1.ShowDialog()
        Me.Show()
    End Sub

For a Timer, Stop() and Enabled = False does the same thing...these are equivalent.  Pick ONE format and stick with that.  =)

You can't dispose of a Form and then later show it again.  If you want it to "go away" then Hide() it:

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

        Me.Hide()

        Dim member1 As New member_form ' launch other form      
        member1.ShowDialog()

        Me.Show()
    End Sub

What is the member_form doing?  Does it make the user login again?  It seems like you should probably be checking the DialogResult() property of the "member1" form to determine whether you should be exiting the application completely or showing the original form again...
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Article by: Kraeven
Introduction Remote Share is a simple remote sharing tool, enabling you to see, add and remove remote or local shares. The application is written in VB.NET targeting the .NET framework 2.0. The source code and the compiled programs have been in…
Introduction When many people think of the WebBrowser (http://msdn.microsoft.com/en-us/library/2te2y1x6%28v=VS.85%29.aspx) control, they immediately think of a control which allows the viewing and navigation of web pages. While this is true, it's a…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now