Link to home
Start Free TrialLog in
Avatar of Anandthirtha
Anandthirtha

asked on

How to get idle/inactive time of application / exe program running on PC?

Requirement is to kill the program if particular application/exe is idle/inactive (not in use) for 20 minutes. I’m able to kill the process but how to get the idle time? I’m coding in Visual Basic 6.0. Please help me out if you’ve some code or suggestions.
ASKER CERTIFIED SOLUTION
Avatar of gbzhhu
gbzhhu
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of anthonywjones66
anthonywjones66

Create this module in your project

' Module idle timing
Private Declare Function SetWindowsHookEx Lib "user32.dll" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32.dll" (ByVal hHook As Long) As Long
Private Declare Function CallNextHookEx Lib "user32.dll" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, lParam As Any) As Long
Private Const WH_KEYBOARD As Long = 2
Private Const WH_MOUSE As Long = 7


Public Sub IdleHook()
mhMouseHook = SetWindowsHookEx(WH_MOUSE, AddressOf Mouse_IO, 0&, App.ThreadID)
mhKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, AddressOf Keyboard_IO, 0&, App.ThreadID)
End Sub

Public Sub IdleUnhook()
ApiRaiseIfNull UnhookWindowsHookEx(mhMouseHook)
ApiRaiseIfNull UnhookWindowsHookEx(mhKeyboardHook)
End Sub

'--- Api Callbacks ---
Private Function Mouse_IO(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Mouse_IO = CallNextHookEx(mhMouseHook, nCode, wParam, lParam)
mlIdleTime = 0         'we've had activity, so reset ticks
End Function

Private Function Keyboard_IO(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Keyboard_IO = CallNextHookEx(mhKeyboardHook, nCode, wParam, lParam)
mlIdleTime = 0
End Function

Public Function IncrementIdleTime() as Long
mlIdleTime = mlIdleTime + 1
IncrementIdleTime = mlIdleTime
End Function



Now on your main form add a timer 'tmrIdle' give it an interval of say 5000 milliseconds

In the form load add:-

IdleHook

and the form unload:-

IdleUnhook


and add the sub

Private Sub tmrIdle_Timer()
Dim lTicks as Long

lTicks = IncrementIdleTime

If lTicks > 240 Then
  ' do stuff when idle detected
End If

End Sub

Anthony
Avatar of Ark
Exactly -> ("Check if an Application is Idle for a Period of Time") http://www.freevbcode.com/ShowCode.Asp?ID=3297

>>
Exactly -> ("Check if an Application is Idle for a Period of Time") http://www.freevbcode.com/ShowCode.Asp?ID=3297
<<

Yikes whatever you do don't use this.  Slap the code in a form and fire it up.  Open up task manager and see how much idle time your CPU doesn't have while the app 'Idles'.

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

Public Sub CheckInputIdle(ByVal TimeOut_InSec As Long)
  Dim t As Long
  t = Timer
  Do While bCancel = False
     If GetQueueStatus(QS_INPUT) Then
        t = Timer
     End If
     DoEvents
     If Timer - t >= TimeOut_InSec Then Exit Do
     Sleep 200
  Loop
  If bCancel = False Then MsgBox "Process idle is for " & Timer - t & " sec."
End Sub
Now try using a real app with that loop in place and you'll find that it's response is sluggish.

DoEvents loops are evil.  Avoid as much as possible.  Definitely don't use one that last for the entire lifetime of your application.

Anthony.
Hello Anthony
Actually, GetQueueStatus is standard API way to determine idle status of a window. Thre is WindowProc main message loop in all other languages except VB, where GetQueueStatus can be placed. I tried to simplify this one for VB. For correct (non-simplified) using you have to subclass window and put this api function inside WinProc callback (without Do...Loop cicle, of course :).
PS - quick searching over the net gave me this sample:
http://thecodeproject.com/dialog/idledialog.asp
Ark,

In order for GetQueueStatus to be used in this way you would have to be in a WindowProc to be guaranteed to see all Input messages.  You would also have to sub-class ALL the applications windows and by 'windows' I mean to mean anything with a windowproc not just each form instance.

Sub-classing windows (whilst at times is useful) is best avoided if there is another way to accomplish the same thing.  Frankly if you have to sub-class every window then VB just isn't the language to be using.  The links you posted rely on code running continuously while the application is 'idle'.  This simply isn't a good idea (standard or not).

The code I posted doesn't require a Do Events loop, wierd sub-classing code or putting the thread to sleep and is very lightwieght on the processor.

Anthony.
Know it's not my question, but Anthony your missing a sub or function "ApiRaiseIfNull"

It does look a good answer :) I'm looking forward to seeing the rest, just wish you didn't destroy other peoples confidence when evaluating others' answers:$
Another minor problem is when the program stops running, as I move the mouse and keyboard the VB6 titebar flashes between "design" and what appears to be "running"...might be related to the ApiRaiseIfNull missing though

Matt
Matt,

Should have taken out the ApiRaiseIfNull it is part of my Error handling infrastructure and not necessary for the point being demonstrated.

This would do.

Public Sub IdleUnhook()
UnhookWindowsHookEx mhMouseHook
UnhookWindowsHookEx mhKeyboardHook
End Sub

As to destorying other peoples confidence I do have the bad habit of responding impulsive but light heartedly to bad advice.  :( Problem is text loses some of the spirit in which some comments are ment.  Still bad advice is bad advice and I feel it would be worse to say nothing at all.  I may not have the social skills to convey that in the most tactful manner but most people aren't here for pleasent evenings of conversation anyway.

As to the titlebar flashing, this is the sort of thing you probably don't want running in an IDE session.  I use the following code to detect that the code is running in an IDE:-

Public mbInDesign as Boolean

Public Sub Main()

SetInDesign

End Sub

Private Function SetInDesign() As Boolean

Static nCallCount As Integer

nCallCount = nCallCount + 1

Select Case nCallCount
    Case 1: Debug.Assert SetInDesign()
    Case 2: mbInDesign = True
End Select

nCallCount = 0

SetInDesign = True

End Function


In the form load add:-

If mbInDesign then IdleHook

and the form unload:-

If mbInDesign Then IdleUnhook

Anthony.


I enjoy the pleasent conversations, but then again i'm 17 and don't have a job resting on any of this advice, so what do I know :P...

It looks like this question will probably hit EE cleanup, but whichever answer I find to be the best, I can vouch for as an unbiased impartial person :P

I'm trying to add an idle routine to a chat program that should notify the client when the user is away, sort of how MSN Messenger does it.

Will check your code when I come home again from school