TTimer Stops by itself - delphi

I am using TTimer in my software and it supposed to run forever 24/7. Nowhere in my software is that timer disabled or stopped from running. It's main function is to update table's value. It is started as soon as the software is run and from that point on TTimer should not stop. However, after running for over a month, that TTimer mysteriously stops running. The software is run on Windows 7 and the software is developed on Delphi 2010 XE. I've searched my code to see what might be causing it, but I can't figure out what.
Timer1.Enabled:=true;
That's how the timer is started.
UPDATE:
After doing some investigation, I found out that the TTimer never stopped, but there is another issue. My TStringGrid table on a TForm just simply don't show any values being updated. Further, I also found out that my TList List I am using to store list of data item is being destroyed somehow that the list becomes empty. But the data items in the list are not deleted anywhere in the code once they are loaded only when the program is started.
Everytime I update the TStringGrid on the form, I run through my TList items from 0 to count-1 location. So, if there is no item in the TList in the memory, my code simply skips
over the display part and thus nothing gets updated on the TStringGrid.
Something like:
If (List.count>0) then
begin
//Display values in TStringGrid;
end;
But while the software is still running, I was able to reload my list of items from a file
back into TList list and my software started to work like it supposed.
I hate to say the ugliest word programmers hate the most. I am afraid I may have a memory leak. Anyone think so?
Any help will be greatly appreciated. Thanks.

I bet it stops 49 days after reboot. When Windows GetTickCount wraps around. Sure you're not doing a check that would fail based on this?

TTimer is just a wrapper around the Windows SetTimer() API which I believe will run forever.
I suspect that the timer still runs, but the event handler that it fires is failing to operate as desired.

I'm not sure why it's stopping after a month; I'd suspect (as Erik said) you have something using GetTickCount() that's failing after the wraparound at ~49 days.
As a general rule, though, it's better to stop/start the timer to prevent a delay from causing a timer message to be dropped:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
try
// Do whatever on timer event firing
finally
Timer1.Enabled := True;
end;
end;
You might try this instead of just allowing it to run constantly; if it is a bug in the TTimer code (I don't see anything after a quick scan of XE's TTimer implementation), stopping and starting may reset things to prevent the failure.

You should free memory allocated after the job is done:
Timer1.FreeOnRelease() or .Free();

Related

Delphi Application Run

The Application.Run procedure calls an infinite loop that handles windows messages:
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated;
The Terminated property can be set to true only through Application.Terminate procedure, which sends PostQuitMesage.
I would like to change the message handling loop so that I can directly stop it using the global variable (without using messages queue):
var MyTerminated:Boolean
....
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated or MyTerminated;
The question is, is it possible to make the program use your own version of Application.Run?
"Terminated" property is read-only. However it is a direct getter of the FTerminated field, therefore Application.Terminated directly reads from the boolean field. While the language disallows setting Application.Terminated, you can set the boolean value at that address using a pointer:
PBoolean(#Application.Terminated)^ := True;
You may also consider using Halt, which will pass over the message loop completely, for a more abrupt but less hacky solution.
Yes, you can make your application use own version of Application run, but this practice is discouraged, because it changes normal program flow, designed by the architects of Delphi.
Directly stopping Application.Run signifies that there is a need to restart Application.Run later, for example, after some action that is wanted to be done from the main thread. This makes the program puzzled, harder to understand by peer programmers and more error prone as a whole.
The program design should be simple and straightforward. If an application is big, for example two million lines of code, the overall design of the execution flow should be simple anyway:
If you need to do some longer actions, do them from the worker threads;
If you need to do an instant actions, do them from your main form or from the other forms.
So a Delphi application main loop should only be exited on the overall application exit, which is done by the PostQuitMessage. We don't have to avoid this message.
The reason why PostQuitMessage is wanted to be avoided, is probably an instantaneous exit. This is not how VCL applications are supposed to run. If one doesn't need forms (for example for a Windows Service application), just don't use the TApplication class an don't run forms, just make your own message loop based on MsgWaitForMultipleObjects.

Delphi Application Z Order and windows ghosting feature

To demonstrate the problem I created a simple application that consists of 4 blank forms. One of them (Form1) is the main and shows automatically. Showing other forms I put in the code project:
…
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.CreateForm(TForm3, Form3);
Application.CreateForm(TForm4, Form4);
Form2.Show;
Form3.Show;
Form4.Show;
Application.Run;
end.
On the second form (or any other) Form2 I put a button that, when clicked, synchronously executes long (>5 seconds) SQL query (any, I open the stored procedure). Is it synchronous, that is, the app hangs for at least 10 seconds. At this time triggers the windows ghosting. Windows creates Windows Ghost for all Windows stale apps that you can minimize, move and close. After executing a query, the application resumes processing messages and everything returns to its original state.
It must be so, but for me it is not. In fact, after defrosting program Z-order of the Windows is changed, the order of the Windows mixed. Moreover, the top (or bottom) can get out of any window, I have not noticed any pattern. It seems that Windows does not correctly restore the Z-order when cancelling the ghosting.
This behavior is also found in Delphi 2007. Note that under the Delphi debugger mode ghosting is disabled and you will not see this behavior. Run the application without debugger to see this behaviour.
After disabling mode ghosting with DisableProcessWindowsGhosting - everything works correctly.
But I don't want to completely disable this mode, the program often hangs for more than 5 seconds, and this mode is useful: the user is still nicer to look at Windows that somehow redrawn than not redrawn at all. To rewrite half the program code for asynchronous execution of queries is very long and time-consuming task, I'm afraid we don't have enough resources, although it is undoubtedly the right approach.
MainFormOnTaskBar property has nothing to do with my question, there is no such property in delphi 7.
Does this effect anyone else besides me? If not – where did I go wrong? How to force Windows to properly reconstruct the sequence of Windows?
There is exactly one way to avoid your application becoming unresponsive. Don't block the main thread.
You are asking how to block the main thread, and keep the application responsive. The only way for an application to remain responsive is for it to process its message queue in a timely fashion. You simply cannot have it both ways. You can't both process messages and not process messages.
Probably the z-order gets changed because your application is not able to process the various clicks you make while the application is not processing messages. And then processes them when the application starts processing messages again.
Conclusion: if you want your application to remain responsive, arrange for the long running tasks to run asynchronously with respect to the main thread.
Disable the windows ghosting "feature" completely and it will fix the z-order issue (and other annoyances of this feature).
The Windows API function to call is named DisableProcessWindowsGhosting, in user32.dll: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-disableprocesswindowsghosting
Allen Bauer, former Delphi architect, described why this z-order issue happens. The page is not available anymore, but WayBackMachine comes to the rescue:
https://web.archive.org/web/20161128155906/https://community.embarcadero.com/blogs/entry/popupmode-and-popupparent-295
This function is not imported in Winapi.Windows.pas (at least not up to Delphi 10.2), so you need to do it yourself. Declare a procedure that imports user32.dll's DisableProcessWindowsGhosting function dynamically and call it before your application starts, like this:
procedure DisableProcessWindowsGhosting;
var
DisableProcessWindowsGhostingProc: procedure;
begin
DisableProcessWindowsGhostingProc := GetProcAddress(GetModuleHandle('user32.dll'), 'DisableProcessWindowsGhosting');
if Assigned(DisableProcessWindowsGhostingProc) then
DisableProcessWindowsGhostingProc;
end;
begin
// Disable windows ghosting feature (XP and newer)
DisableProcessWindowsGhosting;
Application.Initialize;
...
end.

In Delphi 2010, the Click event processed despite the corresponding button being disabled

Consider the following piece of code executed within the OnClick event of a given button:
procedure TForm1.Button1Click(Sender: TObject);
begin
button1.enabled := false; //Line 1
application.processmessages; //Line 2
Sleep(3000); //Line 3
button1.enabled := True; //Line 4
Release; //Line 5
end;
In Delphi 2010, if after clicking this button you manage to perform yet another
click on it while the execution is busy in Line 3, the subsequent click
event will apparently get stored in the queue of commands, thus when
Release(Line 5) procedure is called, the application will attempt to process
it. Consequently the click event will be triggered once again. The second time
around, the button component has already been destroyed, hence "access violation" error get's raised.
The whole concept of acknowledging the second click by the system when the respective
button is disabled does not seem to be sound. Any explanations to this shady behavior?
The system is behaving exactly as designed, but be aware that your code is going against all sound design principles. Specifically the use of Sleep and ProcessMessages in an input event handler are both to be frowned upon.
The reason that the program behaves this way is as follows:
The user generates an input message by clicking the mouse.
This input event is placed in the input queue for the appropriate thread.
That thread is not servicing its input queue (it is sleeping) and so the input message, which is a mouse down, mouse up combo, sits there.
The thread wakes up and enables the button.
The button OnClick handler returns and the application's message loop continues.
In due course the mouse down and mouse up messages are processed (before the CM_RELEASE message) and so the button OnClick handler runs again.
The button OnClick handler calls ProcessMessages which then handles the CM_RELEASE and kills the form.
BOOM!
The whole concept of acknowledging the second click by the system when the respective button is disabled does not seem to be sound.
The key point is that the enabled state of the button is checked when the input message is processed and not when the input message is generated. It has to be this way because input messages are extremely low level things, and it's only the application that can interpret them as things like button clicks.
There are plenty of ways to fix your code, but I'm loathe to suggest any because this is clearly code for illustration. But I will say that all sound solutions will involve the removal of the calls to Sleep and `ProcessMessages.
During the Sleep your application is non responsive. The click message is queued and is only processed after you re-enabled the button (actually after the event handler method is fully executed and the application has gone idle again).
To solve it, also execute Application.ProcessMessages after sleep, before enabling the button. That will empty your message queue first and will discard the click message.
Or just don't enable the button at all. Why would you, if you're going to release the form anyway?
A (probably) better solution would be to execute the Sleep in a separate thread, but since Sleep here is probably just a stub for some real code, it's hard to say how much effort it will take to do that.
Anyway, your current application isn't good, and calling Application.ProcessMessages in situations like this is likely to generate 'random' bugs once in a while. The best you can do is limit the risk, but there is no good way to solve it, other than radically change this implementation.

ShellExecute blocks (never returns) when firewall denies internet access

I've been using ShellExecute to open URLs in a browser, invoked from a modal windows' code, like this:
procedure TfmAbout1.BtnHomePageClick(Sender: TObject);
begin
inherited;
if ConnectedToWeb then
ShellExecute(Handle, 'open', URL_PRODUCT_HOMEPAGE, nil, nil, SW_SHOWNORMAL);
end;
But on machines with the Zone Alarm firewall, the user may get a pop-up prompt to allow or deny my application access to the Internet. When the user clicks "Deny", then ShellExecute never returns... my application is then hung & the process has to be shut down externally (i.e., from Task Manager).
What can I do to either anticipate or prevent this? I need something like a ShellExecute that is non-blocking in this situation.
I'd appreciate any ideas.
So you want to work around a bug in Zone Alarm?
I'd probably check for the Zone Alarm version, and depending on it, have a small 'bootstrap' program perform the ShellExecute on my behalf. That would only hang the small bootstrap program.
You could kill that bootstrap program (since your parent process is the owner) if it hasn't returned after a time out (15 or 30 seconds would do).
You could even warn the user that is has a buggy version of Zone Alarm :)
Edit: Note: I think killing a process is safer than killing a thread.
Well, run ShellExecute in a thread. In some time you could see if it completed or not. If not, you could just terminate it.

Why doesn't TTimer work correctly?

As you can tell, I am having a little trouble with the TTimer VCL component within my application developed using Delphi 2010.
Whenever I call:
Self.Timer1.Enabled := False;
Self.Timer1.Enabled := True;
The timer component does seem to indeed halt, but, it never starts up again. Like, it never calls the OnTimer event.
I am using it in conjunction with the Indy10 IRC component (TIdIRC).
Thanks in advance :)
You are also aware that most Indy calls are blocking? A timer works by sending Windows Messages which means the messages need to be processed in order for the timer to fire. If the application/main thread blocks it does not process messages and timers do not get fired. Maybe this is your problem?
Also if your application is a Windows Service or a console application the messages might not be processed.
Take Indy out of the equation and see if you can get it to basically throw up the hint every 10 seconds. If not, you're doing something wrong. Like mis-interpreting the interval time. For instance, "(10000/10 seconds)" is not very clear. I think you mean "10000ms which is 10 seconds" but you don't show your code, so I can only guess. Delphi won't guess, it will do what you tell it.
Anyway, make a sample app with a form, button, edit box and a ttimer and experiment. Get the timer stuff working, before you introduce the Indy stuff. Then you know whether you've got a TTimer problem or an Indy problem.
You must make sure that you call these methods of TTimer only in the context of the main thread. Since Indy uses threads it may well be the case that this rule is violated. To find out easily you can call Assert when the code is executed in the context of another thread:
Assert(GetCurrentThreadId = MainThreadId);
Self.Timer1.Enabled := False;
Self.Timer1.Enabled := True;
I don't know what Self is referring to in your code, but it's worth trying.
I created a Timer in a separate thread and everything is working well again :)
Thanks guys, I learned a lot of stuff.

Resources