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

Friday, October 30, 2026

Anchors - blog page internal markers

This morning I had a hankering to read about the last song that I ever completed, "Maeve's song". This appears in blog #19321, and it references blog #19212. But when I clicked on the link to the earlier blog, I found myself reading about Passover 2025; I asked myself what this has got to do with "Maeve's song".

It turns out that blog #1921 is one of those that is about two completely different subjects (maybe now I would write two separate blogs), and the material about Maeve starts only halfway through the blog. How could I access this part immediately? I remembered that one can add anchors to HTML pages in order to mark section headings and that these anchors are valid targets for a browser. I could see immediately that there were two problems with the code within the Blog Manager Program (BMP) as it currently stands: one trivial and one more complicated that I didn't know how to solve.

The trivial problem was that the link would be <A href="https://nbnewman.blogspot.com/2025/04/passover-begins-tonight.html#maeve" target=_blank> and there is no blog entry with that address. Obviously the anchor (#maeve) has to be extracted so that the correct blog entry can be found.

But the complicated problem was how to pass the anchor to the browser. It turns out that this is almost as trivial as extracting the anchor. Obviously the anchor has to be passed as a parameter to the TShowBlogContent.Create constructor and saved in a form variable. Instead of opening/displaying the web page by means of the command 'web.navigate (s)'where s is the name of a text file containing the blog's text, that variable s is modified in the line prior to this: the anchor is attached to the file name. So the string being passed to web.navigate would be something like 'c:\db\blog\data\blog_1921.htm#maeve'. Easy peasy. The only thing that I missed originally was the anchor tag within the target blog: it should be <a id="maeve"></a>.

Internal links
[1] 1932
[2] 1921



This day in blog history:

Blog #Date TitleTags
42130/10/2011Sir Jimmy SavileObituary, RIP
42230/10/2011Succulent SundayCooking, Slow cooker
77130/10/2014Another good ideaERP
118130/10/2018Losing an old friend, gaining a new oneComputers
143330/10/2021Binaural beats and deep sleepCPAP, MP3, The brain, Binaural beats, Sleep
154330/10/2022Adding more old blog entriesProgramming, Meta-blogging, Blog manager program
202830/10/2025Counting beats with Van der Graaf Generator (5) - After the floodVan der Graaf Generator