我有这个有趣的错误,我需要修复。我已准备好几个解决方案但在实施其中一个解决方案之前,我想问为什么会出现这样的错误。我问的原因是因为我无法复制这个bug所以我正在实施一个故障恢复解决方案。
我得到的错误是TList
个对象:" [EListError]列表索引越界(0)"。
TList
包含少量TForm
个对象,我们将其隐藏,然后在该操作后立即将其添加到TList
。现在我们想让它们再次可见并且自由,并且在此之后为TList
。当我们想要再次显示表单时会发生错误。
所以有一个循环而TList.Count
有一个值。代码进入循环并发生上述错误。不高于错误意味着特定索引上没有项目,但列表计数大于零,这怎么可能?
这里唯一不常见的事情可能是for循环倒计时,以便我们以相反的顺序显示表单。
for ii := FormListObject.Count - 1 downto 0 do begin
// Error happens here
TForm(FormListObject[ii]).Show;
end;
你认为这是一个gui线程问题,一个子项目问题,或者某种形式被破坏/杀死,列表有一个死引用?我仍然认为这个错误意味着索引0处没有TList项,死引用应该触发访问冲突错误,不是吗?
答案 0 :(得分:3)
我得到的错误是在TList对象上:" [EListError]列表索引越界(0)"。
这很容易理解。您有一个空列表,并尝试访问索引为零的元素。
使用调试器告诉您哪个列表为空。获取调试器以打破异常。然后尝试找出为什么你的代码假定列表不是空的时候不是这样。
答案 1 :(得分:3)
三个可能的原因:
内存覆盖恰好覆盖"可访问"记忆(所以没有AV)。但是会触发对FormListObject
无效的更改。
其他东西(类似于OnShow事件)实际上正在从FormListObject
中删除项目。它不必是一个不同的线程;但当然这也是可能的。 注意:虽然你问它是否是GUI线程问题,但我真的怀疑是这种情况,因为你必须专门从不同的线程调用GUI代码来实现这种可能性。
重入代码导致在循环中间更改FormListObject
。当您在处理另一条消息时调用Application.ProcessMessages
时会发生这种情况。 我的调试感正在抽搐。即使您不对Application.ProcessMessages
罪行感到不满,您表现形式上的某些组成部分也可能是。
PS :我在评论中注意到你说你已经有了堆栈跟踪。进一步检查堆栈跟踪,看看您的代码是否可重入。
由于您无法重现该问题,因此需要额外的调试日志记录才能获得更多信息。假设你有一个只附加到调试文件的Log方法。 (log方法应输出当前线程ID以确认线程问题。)添加调试日志记录,如下所示:
Log('Start loop');
for ii := FormListObject.Count - 1 downto 0 do begin
// Error happens here
TForm(FormListObject[ii]).Show;
end;
Log('End loop');
封装您的FormListObject
,以便删除/删除/清除项目的任何代码都会通过您控制下的代码。即不要暴露对底层列表的直接访问,这将允许恶意代码在没有你的知识的情况下更改列表。然后在任何可以删除项目的方法上添加日志记录:
Log(Format('(%p)FormListObject.Clear', [Pointer(Self)]));
记录FormListObject
个实例的创建/销毁可能也很有用。
当问题发生时,您必须分析日志文件以确定出现了什么问题。例如,如果我对问题的最可能原因的预感是正确的,您可能会发现以下模式(注意每个条目都在同一个线程上):
(Thread X) Start Loop
(Thread X) Start Loop
(Thread X) End Loop
(Thread X) ($........) Clear
我增加了第三种可能性;我实际上有预感是最可能的原因 还添加了一些特定的调试建议以缩小范围。