If you would like to get paid for surfing the web, jump to http://www.codeoftheweek.com/paidsurf.html
Want to get up to speed on the latest Visual Basic programming? Includes Visual Basic 6 and Visual InterDev 6. Check out our training programs at http://www.codeoftheweek.com/vbtraining.html
This issue expands on last weeks issue to further explain how the API calls perform their magic to monitor keyboard and mouse activity.
The only limitation we are aware of with this approach is that it can only monitor the keyboard and mouse activity within your application, not across all Windows applications.
A good use of this routine is to log a user out of an application after a specified period of inactivity.
If you have any questions about using this module, let us know at questions@codeoftheweek.com
Public Enum InactivityMode
This enumerator is used as a parameter to ActivityRef. Currently eMode can only be one of two values: imKeyboard or imMouse. imKeyboard will specify keyboard activity and imMouse will specify mouse activity.
Public Function ActivityRef(eMode As InactivityMode) As Date
Retrieves the last date/time activity was found for the device specified by eMode. Currently eMode can only be one of two values: imKeyboard or imMouse.
Public Function SetInactivityHook() As Boolean
Creates the hooks to monitor the keyboard and mouse activity. Returns True if successful and False if unsuccessful.
This routine calls several API calls, most importantly SetWindowsHookEx which allows your application to hook into the keyboard and mouse activity chain for monitoring. If you really wanted to you, could actually perform some processing of the activity (keystrokes would probably be more likely) other than just watching for those events to occur. When using this API call the keyword AddressOf is used to return the address of your callback routine for passing into SetWindowsHookEx. The callback routine must be in a standard module; it can not be in a form or a class module. For those of you unfamiliar with callbacks see the section below describing them.
It is critical that you release the hooks before your application ends (using ResetInactivityHook) or you will create some spectacular system crashes.
Public Sub SetParameters(o As cInactivtyWatch, lInactivityDelay As Long, lInactivityInterval As Long)
This provides the module with a way to know where to raise the event when inactivity occurs. Refer to last weeks issue for details on the cInactivityWatch class. This routine also saves the Delay amount and Interval amounts for later use (refer to last weeks issue for more details).
Public Sub ResetInactivityHook()
Releases the monitoring of the keyboard and mouse, which is the reverse of the process started with SetInactivityHook.
The API call UnhookWindowsHookEx is used to accomplish this task. It uses the handles received in SetInactivityHook to cancel the monitoring of the keyboard and mouse.
See above descriptions.
Some other interesting notes about this module:
For those of you that have not used callbacks before here is a brief description. When your application needs to watch for some event to occur there are primarily two ways to accomplish this.
The first is to create a loop in your code which repeatedly checks for some condition. This usually is not a desirable way to accomplish this tasks since it will eat up all the available CPU time while processing the code in your loop.
The second is to provide the address of a routine called a CALLBACK to the routine which will can determine the event you are watching for in an efficient manner (In our case it was the keyboard/mouse hook). When that routine gets the event it will make a call to your callback routine to let your code know that the event has occured. This is a very desirable way to perform this task because your code can continue on its way and it will be notified if and when the event you are watching for occurs.
The simplest form of a callback is probably the timer control in Visual Basic. When you put the timer control on your form the Timer event fires when appropriate. This is in essence a callback.
If anyone has a better description of callbacks, pass it along and we will pass it along to our readers. Send an email to callback@codeoftheweek.com
The sample included at http://www.codeoftheweek.com/issues/issue102 shows how to use the above class. It provides a form that shows keyboard and mouse inactivity timers.
' See zip file at http://www.codeoftheweek.com/issues/issue102 for a detailed sample.
To see an easy-to-use class wrapper around this module you can download the class module at http://www.codeoftheweek.com/issues/issue102
Create a new module and paste this source code into it. You should name this class module basInactivity. If you have any questions, email us at help@codeoftheweek.com
'---------------------------------------------------------------------- ' ' Module Name: basInactivity ' Written By: C&D Programming Corp. ' Create Date: 8/99 ' Copyright: Copyright 1999 by C&D Programming Corp. Source ' code may not be reproduced except for use in a ' compiled executable. All rights reserved. If ' you would like to reprint any or all of this ' code please email us at info@codeoftheweek.com '---------------------------------------------------------------------- Option Explicit ' provide a link into the class to allow this module to raise an event when the ' inactivity time becomes true. Private oInactivityWatch As cInactivtyWatch ' Windows hook stuff Private Declare Function SetWindowsHookEx Lib "user32" 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" (ByVal hHook As Long) As Long Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Integer, lParam As Any) As Long Private Const WH_KEYBOARD = 2 Private Const WH_MOUSE = 7 ' Timer API Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long '============================================================================ Private mlInactivityDelay As Long ' in seconds Private mlInactivityInterval As Long ' how often to check for inactivity in seconds (we ' recommend this is 5 seconds or less) Private mhHookMouse As Long ' handle for mouse hook Private mhHookKeyboard As Long ' handle for keyboard hook Private mlTimerID As Long ' timer ID value Private mbMouseActivity As Boolean ' flag set by any mouse activity Private mbKeyboardActivity As Boolean ' flag set by any keyboard activity Private dtMouseReference As Date ' last time of mouse activity Private dtKeyboardReference As Date ' last time of keyboard activity ' some enumerators to make things easier. Public Enum InactivityMode imKeyboard imMouse End Enum Public Function ActivityRef(eMode As InactivityMode) As Date If eMode = imKeyboard Then ActivityRef = dtKeyboardReference End If If eMode = imMouse Then ActivityRef = dtMouseReference End If End Function Public Sub SetParameters(o As cInactivtyWatch, lInactivityDelay As Long, lInactivityInterval As Long) Set oInactivityWatch = o mlInactivityDelay = lInactivityDelay mlInactivityInterval = lInactivityInterval End Sub Public Function SetInactivityHook() As Boolean On Error Goto Handler ' set mouse hook If mhHookMouse = 0& Then 'not already set mhHookMouse = SetWindowsHookEx(WH_MOUSE, AddressOf MouseProc, 0&, App.ThreadID) End If ' set keyboard hook If mhHookKeyboard = 0& Then 'not already set mhHookKeyboard = SetWindowsHookEx(WH_KEYBOARD, AddressOf KBProc, 0&, App.ThreadID) End If ' initialize some variables dtMouseReference = Now ' dtKeyboardReference = Now mbKeyboardActivity = True 'initialize mbMouseActivity = True 'initialize If mlTimerID = 0& Then 'not already set ' take default of 5 seconds If mlInactivityInterval = 0 Then mlInactivityInterval = 5 End If ' this is a built-in limit of the settimer function (actually it is probably ' 65535, but we figured 60 seconds would be adequate for this application) If mlInactivityInterval > 60 Then mlInactivityInterval = 60 End If mlTimerID = SetTimer(0&, 0&, mlInactivityInterval * 1000, AddressOf TimerProc) End If SetInactivityHook = True Exit Function Handler: SetInactivityHook = False Exit Function End Function Public Sub ResetInactivityHook() Dim lRet As Long If mlTimerID <> 0& Then KillTimer 0&, mlTimerID mlTimerID = 0& End If If mhHookMouse <> 0& Then 'not already unhooked lRet = UnhookWindowsHookEx(mhHookMouse) If lRet <> 0& Then mhHookMouse = 0& End If If mhHookKeyboard <> 0& Then 'not already unhooked lRet = UnhookWindowsHookEx(mhHookKeyboard) If lRet <> 0& Then mhHookKeyboard = 0& End If End Sub Private Function MouseProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long If nCode < 0 Then MouseProc = CallNextHookEx(mhHookMouse, nCode, wParam, lParam) End If mbMouseActivity = True End Function Private Function KBProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long If nCode < 0 Then KBProc = CallNextHookEx(mhHookKeyboard, nCode, wParam, lParam) End If mbKeyboardActivity = True End Function Private Function TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long) As Long ' if we had activity, update our time reference and continue. If mbKeyboardActivity Then dtKeyboardReference = Now 'reset reference mbKeyboardActivity = False End If If mbMouseActivity Then dtMouseReference = Now 'reset reference mbMouseActivity = False End If If Not mbMouseActivity And Not mbKeyboardActivity Then 'check for no activity If (Abs(DateDiff("s", Now, dtMouseReference)) >= mlInactivityDelay) And _ Abs(DateDiff("s", Now, dtKeyboardReference)) >= mlInactivityDelay Then oInactivityWatch.UserInactivity ' force counter to reset in case the user decides not to abort the ' exiting of this routine. This should happen anyway since the user ' would have to hit a key or move the mouse to cancel the operation. mbKeyboardActivity = True mbMouseActivity = True End If End If End Function
If you would like to get paid for surfing the web, jump to http://www.codeoftheweek.com/paidsurf.html