The Case of OnIdle
by
on 28-Oct-2010 at 03:33 AM (6658 Views)
From time to time the question arises "how can I terminate my program when no activity took place?".
Visual DataFlex offers two features named DFTIMER and cIdleHandler but both do not really cover the topic. Let us look closer at these two options and find a real solution.
A Timer (DFTIMER)
A timer (DFTIMER) fires a message every NN milliseconds no matter there is activity or not. In the list of tables treeview in Database Explorer we use an object of this class to make a kind of idle handler after NN seconds. Database Explorer can do this because you navigate through the treeview and on each change in the navigation by keyboard we reset the timer (stop and start the timer again). It works but it is quite a one control solution and not a more general one.
An Idle Handler (cIdleHandler)
The cIdleHandler fires ONE message as soon as the program becomes idle. It is specifically added to the product to let tool bar buttons show enabled or disabled based on the current program status (like "is save wanted or not"). Since the message fires immediately after inactivity starts this is not the solution we are looking for.
Can You Query the Inactivity?
It is possible to do this indeed. The Windows kernel exposes a function named GetLastInputInfo. The returned value (a member of a struct) contains the tickcount since last input was done in the current session. You need to compare this value with the result of the system wide GetTickCount function to find out the idle timeout.
An Idle Time Timer
With the above information we can write the following class:
This can be used to make an object like:Code:Struct tLastInputInfo UInteger cbSize DWord dwTime End_Struct External_Function GetLastInputInfo "GetLastInputInfo" User32.dll Pointer pLastInputInfo Returns Integer External_Function GetTickCount "GetTickCount" Kernel32.dll Returns Integer Use Dftimer.pkg { OverrideProperty = TimeOut Visibility = Private } { OverrideProperty = Timer_Message Visibility = Private } { OverrideProperty = Timer_Object Visibility = Private } { OverrideProperty = Timer_Active_State Visibility = Private } { OverrideProperty = psTooltip Visibility = Private } { OverrideProperty = pbCenterTooltip Visibility = Private } { OverrideProperty = phoTooltipController Visibility = Private } { OverrideProperty = Auto_Start_State Visibility = Private } { OverrideProperty = Auto_Stop_State Visibility = Private } { OverrideProperty = Delegation_Mode Visibility = Private } { OverrideProperty = peNeighborHood Visibility = Private } Class cProgramIdleTimer is a DfTimer { MethodType = Event } Procedure OnIdle Integer iIdleTime End_Procedure { Visibility = Private } { MethodType = Event } Procedure OnTimer Integer iwParam Integer ilParam Integer iIdleTime iInActivityTimeOut Get IdleTime to iIdleTime Get piInActivityTimeOut to iInActivityTimeOut If (iIdleTime >= iInActivityTimeOut) Begin Send OnIdle iIdleTime End End_Procedure Procedure Construct_Object Forward Send Construct_Object Set Timeout to 30000 // 30 seconds { Visibility = Private } Property Integer private_piInActivityTimeOut 29000 { DesignTime = False } Property Boolean pbBusy False End_Procedure { MethodType = Property } { Category = "Behavior" } Procedure Set piInActivityTimeOut Integer iTimeOut Set private_piInActivityTimeOut to iTimeOut Set Timeout to (iTimeOut + 100) End_Procedure { MethodType = Property } Function piInActivityTimeOut Returns Integer Integer iTimeOut Get private_piInActivityTimeOut to iTimeOut Function_Return iTimeOut End_Function { MethodType = Property } { Category = "Behavior" } Procedure Set pbEnabled Boolean bEnabled Set Timer_Active_State to bEnabled End_Procedure { MethodType = Property } Function pbEnabled Returns Boolean Boolean bEnabled Get Timer_Active_State to bEnabled Function_Return bEnabled End_Function Function IdleTime Returns Integer tLastInputInfo LastInputInfo Integer iResult iTick iIdleTime Move (SizeOfType (tLastInputInfo)) to LastInputInfo.cbSize Move (GetLastInputInfo (AddressOf (LastInputInfo))) to iResult If (iResult <> 0) Begin Move (GetTickCount ()) to iTick Move (iTick - LastInputInfo.dwTime) to iIdleTime End Function_Return iIdleTime End_Function End_Class
The DFTimer's TimeOut property does need to have a higher value than the piInActivityTimeOutCode:Object oCloseProgramTimer is a cProgramIdleTimer Set piInActivityTimeOut to 5000 Procedure OnIdle Integer iIdleTime Integer iAnswer Boolean bBusy Handle hWnd Get pbBusy to bBusy If (not (bBusy)) Begin Set pbBusy to True Move (YesNo_Box ("No Activity for" * String (iIdleTime) * "milliseconds, Quit?", "Confirm", MB_DEFBUTTON2)) to iAnswer If (iAnswer = MBR_Yes) Begin Send Exit_Application End Set pbBusy to False End End_Procedure End_Object




