Saturday, October 31, 2026

Timing out an application

The OP's "Management ERP" program has several users - very good. Unfortunately this is not so good when I want to transfer an updated version of the program to their server, as the earlier program might be locked by one of the users. I normally get around this by renaming the earlier program (ie renaming manage.exe [as it was then] to manage1.exe) then transferring the new program. But I would be happier if I could delete the earlier version; it might be that the user who had locked this version would continue to use this version.

I came across this article several years ago that is almost useful for my needs. I write 'almost' because it assumes that the application has only one form In the main form of your application, set the KeyPreview property to True so the form will see keystrokes before any other components.... Open up the OnKeyDown method for your form and put the following code [that] calls ResetTheTimer which, in turn, resets the TimeOut variable to zero, then disables and re-enables the Timer. The reason we do form-level processing of the keystrokes is so that no matter which component the user is typing in, keystrokes are always picked up by the form. This is useless if the application has more than one form.

I imagine that I researched this topic further and came across the Windows API function, GetLastInputInfo. This appeared to do exactly what was necessary and didn't depend on one single form. This morning I was occupied with transferring the database of the management program to a different server and so I had to update definitions on each client computer so that the program would know where to find the database. On one computer, I discovered that the management program was running - even though this was Saturday morning and the user was nowhere to be found (and no, it wasn't being run remotely by AnyDesk). This obviously is the user who was always locking earlier versions.

So I asked CoPilot about this and discovered that GetLastInputInfo() returns the tick count corresponding to the last input event received by the current Windows session, not just your Delphi application. This is apart from any bugs that might (and were) in my implementation. So it became necessary to write new code for checking for time-outs solely within my application. The code is split into two (as was the original code in that Delphi Corner article); the timer code is almost the same as my original so I'm not going to show it here. 
// in the main form's OnShow event, add the following lines application.onmessage:= appMessage; fLastActivity:= GetTickCount64; timer2.enabled:= true; // here is appMessage procedure TMainForm.AppMessage (var msg: TMsg; var handled: boolean); var IdleBeforeReset: cardinal; msgname, s, formname: string; begin case msg.message of WM_LBUTTONDOWN: msgname:= 'WM_LBUTTONDOWN'; WM_RBUTTONDOWN: msgname:= 'WM_RBUTTONDOWN'; WM_MBUTTONDOWN: msgname:= 'WM_MBUTTONDOWN'; WM_MOUSEWHEEL: msgname:= 'WM_MOUSEWHEEL'; WM_KEYDOWN: msgname:= 'WM_KEYDOWN'; WM_SYSKEYDOWN: msgname:= 'WM_SYSKEYDOWN'; else msgname:= '-'; end; if msgname <> '-' then begin IdleBeforeReset:= (GetTickCount64 - fLastActivity) div 1000; if IdleBeforeReset > 60 then begin if screen.activeform <> nil then formname:= screen.activeform.Name else formname:= '<None>'; s:= format ('%s idle reset after %d seconds, message = %s, activeform = %s', [CompName, IdleBeforeReset, msgname, formname]); Write2Log (s); end; fLastActivity:= GetTickCount64; end; end;

This code is actually a hybrid of CoPilot's and mine. The first part means that if one of the given events is detected, the variable msgname gets a value. If the event was one of the tracked events, then the code checks to see whether the time out period is about to elapse (this check is made when more than one minute has passed, but looking at this now, it would be far better if it were made when there there is less than one minute remaining to the end of the time out period), and if so, first discovers what the active form is, then writes data to a log file. I intend to check this log file to see which events are happening to whom close to the time out period in which form, and whether the time out actually occurs (this is in the Timer2timer event handler).

I replaced the line IdleBeforeReset > 60 which would have logged any activity after a break of a minute with this line if IdleBeforeReset >= MaxTimeOutValue - 60 that logs only the event that prevented timing out at the last minute. Much more useful.

It will be interesting to see what the log contains.



This day in blog history:

Blog #Date TitleTags
108631/10/2017Tom PaxtonPersonal
127031/10/2019Coincidence or deliberate reference?John Le Carre, Non-fiction books
154431/10/2022Extending cooking length for chickenCooking, Ninja grill
202931/10/2025Implementing CP Prolog - 6Programming, Prolog, CoPilot

No comments: