Subject Re: Form closing when idle
From Mervyn Bick <invalid@invalid.invalid>
Date Sun, 16 May 2021 15:21:03 +0200
Newsgroups dbase.getting-started
Attachment(s) close_on_idle.wfm

On 2021/05/13 18:33, Mustansir Ghor wrote:
> Dear Mervyn sir
>
> I had read in this newsgroup, where there is routine that closes the form when it stays idle for certain time. Sir can you help me to recollect where I could find this routine.

My apologies for taking so long to respond.  A little example form is
attached.  This form uses the CALLBACK command so it will only work with
dBASE Plus 8 and later.

The example uses a keyboard hook and a mouse hook to monitor all
instructions to the form.  Any keystroke or mouse movement resets the
timer.  The idle time before the form closes is set to 2 minutes in the
example. You can obviously set it to your own requirements.

One minute before the form closes a warning count-down is displayed.

The keyboard and mouse hooks are completely stable provided they are
properly detached when the form is closed correctly.

There is one very annoying problem when one uses a keyboard or mouse
hook in a form.  Any error, not necessarily in regard to the hook,
causes dBASE to crash and close.  This is because the hook in Windows is
not detached when dBASE opens the form in the sourcecode editor.

The work-around is to disable both the timer and the hooks while the
form is being developed. The places to do this are marked in the form's
onOpen event handler.

Once you are certain that the form has no problems and is ready to be
used make the timer active and uncomment the line that attaches the hooks.

Mervyn.






clear
#include winuser.h
#define wm_mousewheel 0x020A  //not in winuser.h
** END HEADER -- do not remove this line
//
// Generated on 2021-05-13
//
parameter bModal
local f
f = new close_on_idleForm()
if (bModal)
   f.mdi = false // ensure not MDI
   f.readModal()
else
   f.open()
endif

class close_on_idleForm of FORM
   with (this)
      onMouseOver = class::FORM_ONMOUSEOVER
      onMouseOut = class::FORM_ONMOUSEOUT
      onOpen = class::FORM_ONOPEN
      onClose = class::FORM_ONCLOSE
      height = 18.4545
      left = 25.5714
      top = 1.4091
      width = 62.4286
      text = ""
   endwith

   this.ENTRYFIELD1 = new ENTRYFIELD(this)
   with (this.ENTRYFIELD1)
      height = 1.0
      left = 12.0
      top = 5.7273
      width = 25.7143
      value = "Entryfield1"
   endwith

   this.TEXTLABEL1 = new TEXTLABEL(this)
   with (this.TEXTLABEL1)
      height = 1.0
      left = 28.1429
      top = 0.8636
      width = 29.5714
      text = ""
   endwith

   this.ENTRYFIELD2 = new ENTRYFIELD(this)
   with (this.ENTRYFIELD2)
      height = 1.0
      left = 12.0
      top = 8.7273
      width = 25.7143
      value = "Entryfield2"
   endwith


   function attach
      if this.mhookProc == null
         CALLBACK CLONG MouseHookWndProc(CINT, CUINT, CUINT) OBJECT this
         this.mhookProc = GetCallAddress(class::MouseHookWndProc)
      endif      
      mhInst = GetWindowLong(int(form.hwnd), WH_MOUSE)
      this.mhook = SetWindowsHookEx(WH_MOUSE, this.mhookProc,mhInst, GetCurrentThreadId())
      if this.khookProc == null
         CALLBACK CLONG KeyHookWndProc(CINT, CUINT, CUINT) OBJECT this
         this.khookProc = GetCallAddress(class::KeyHookWndProc)
      endif    
      khInst = GetWindowLong(int(form.hwnd), WH_KEYBOARD)
      this.khook = SetWindowsHookEx(WH_KEYBOARD, this.khookProc,khInst, GetCurrentThreadId())
      return


   function detach
      UnhookWindowsHookEx(this.mhook)
      UnhookWindowsHookEx(this.khook)
      return

   function form_onClose()
      class::detach()
      this.timer.enabled = false
      return
      

   function form_onMouseOut(flags, col, row)
      form.mouseOver = false
      return

   function form_onMouseOver(flags, col, row)
      form.mouseOver = true
      return

   function form_onOpen()
      if type("GetCurrentThreadId") # "FP"
         extern CULONG GetCurrentThreadId() kernel32 from "GetCurrentThreadId"
      endif
      if type("GetWindowLong") # "FP"
         extern CLONG GetWindowLong(CHANDLE, CINT) user32 from "GetWindowLongA"
      endif
         if type("SetWindowsHookEx") # "FP"
       extern CLONG SetWindowsHookEx(CINT, CPTR, CHANDLE, CULONG) user32 from "SetWindowsHookExA"
      endif
      if type("UnhookWindowsHookEx") # "FP"
         extern CLOGICAL UnhookWindowsHookEx(CHANDLE) user32
      endif
      if type("CallNextHookEx") # "FP"
         extern CLONG CallNextHookEx(CHANDLE, CINT, CUINT, CUINT) user32
      endif
      form.mouseOver = true
      this.timer = new Timer( )
      this.timer.parent = this
      this.timer.seconds = seconds()
      this.timer.allow_idle_time = 60 *2 // 2 minutes. value in sconds
      this.timer.onTimer := this.check_action
      this.timer.interval =  1
      this.timer.enabled = true  // set false while developing form ********
      this.mhook = null
      this.khook = null
      this.mhookProc = null
      this.khookProc = null
      class::attach()  // comment out while developing form *********
      return
      

   function check_action
      if int(abs(seconds()-this.allow_idle_time-this.seconds)+1) < 61
         this.parent.textlabel1.visible = true
      else  
         this.parent.textlabel1.visible = false
      endif  
      this.parent.textlabel1.text = 'This form will close in '+int(abs(seconds()-this.allow_idle_time-this.seconds)+1)+' second'+iif(int(abs(seconds()-this.allow_idle_time-this.seconds)+1) = 1,'', 's')
      if seconds()-this.allow_idle_time-this.seconds >1
         this.parent.close()
      endif  
      return

function MouseHookWndProc(hCode, wParam, lParam)
      if hCode = 0  and form.mouseOver = true
         if  wparam = wm_mousemove or ;
             wparam = wm_nclbuttondown or ;
             wparam = wm_lbuttondown or ;
             wparam = wm_rbuttondown or ;
             wparam = wm_mbuttondown or ;
             wparam = wm_mousewheel
             form.timer.seconds = seconds()
         endif
      endif      
      return CallNextHookEx(this.mhook, hCode, wParam, lParam)
      

  function KeyHookWndproc(hCode, wParam, lParam)
      t1 =  wParam   // virtual key code.
      t2 = lParam  // bit 31 = 0 keydown, bit 31 = 1 keyup
      if bitset(lparam,31)  = false  and hCode = 0 and form.mouseOver = true
         form.timer.seconds = seconds()
      endif
      return CallNextHookEx(this.khook, hCode, wParam, lParam)  
      

endclass