Pregunta

I have this interesting bug that I need to fix. I've couple of solutions ready but before I implement one of them I like to ask why such an error may happen. The reason I am asking is because I can't replicate this bug so I am kind of implementing a fail recovery solution.

The error I get is on a TList object: "[EListError] List index out of bounds (0)". The TList contains few TForm objects where we made them invisible then add them to the TList right after that action. Now we want to make them visible again and free and nil the TList after that. Error happens when we want to make the forms visible again.

So there's a loop and the TList.Count has a value. The code gets into loop and the above error happens. Isn't above error implies that there's no item at the specific index but the list count is greater than zero, how could this be?

The only out of ordinary thing here might be the for loop counting down so that we display the forms in reverse order.

for ii := FormListObject.Count - 1 downto 0 do begin
   // Error happens here
   TForm(FormListObject[ii]).Show;
end;

Do you think this is a gui threading issue, a child item issue or somehow one of the forms got destroyed/killed and the list has a dead reference? Still I thought this error meant that there's no TList item at index 0, dead reference should trigger an access violation error, no?

¿Fue útil?

Solución

Three possible causes:

  • A memory overwrite which happens to overwrite things in "accessible" memory (so no AV). But triggers an invalid change to FormListObject.

  • Something else (something like OnShow event) is actually removing items from FormListObject. It doesn't have to be a different thread; but of course that's also possible. NOTE: Although you asked if it's a GUI threading issue, I really doubt this is the case because you would have to be specifically calling GUI code from a different thread for this possibility.

  • Re-entrant code is causing FormListObject to be changed while in the middle of the loop. This happens when you call Application.ProcessMessages while in the middle of processing another message. My debuggey-sense is twitching on this one. Even if you aren't guilty of the Application.ProcessMessages sin, some component on the form you're showing may be.

PS: I notice in a comment you say you already have a stack-trace. Check further down the stack-trace, and see if your code is re-entrant.


Since you're unable to reproduce the issue, you need extra debug logging to get more info. Assuming you have Log method that simply appends to a debug file. (The log method should output current thread ID to confirm threading issues.) Add debug logging as follows:

Log('Start loop');
for ii := FormListObject.Count - 1 downto 0 do begin
   // Error happens here
   TForm(FormListObject[ii]).Show;
end;
Log('End loop');

Encapsulate your FormListObject so that any code to remove/delete/clear items goes through code under your control. I.e. don't expose direct access to the underlying list which would allow rogue code to change the list without your kowledge. Then add logging on any method that can remove items:

Log(Format('(%p)FormListObject.Clear', [Pointer(Self)]));

It might also be useful to log creation/destruction of FormListObject instances.


When the issue occurrs, you'll have to analyse the log file to determine what went wrong. For example, if my hunch about the most likely cause of the issue is correct, you might find the following pattern (note each entry is on the same thread):

(Thread X) Start Loop
(Thread X) Start Loop
(Thread X) End Loop
(Thread X) ($........) Clear

EDIT

I've added a third possibility; which I actually have a hunch is the most likely cause.
Also added some specific debugging suggestions to narrow it down.

Otros consejos

The error I get is on a TList object: "[EListError] List index out of bounds (0)".

This is simple to understand. You have an empty list and are attempting to access an element at index zero.

Use the debugger to tell you which list is empty. Get the debugger to break on exceptions. And then try to work out why your code assumes the the list is not empty when that is not so.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top