2

我有一个有趣的错误需要修复。我已经准备好几个解决方案,但在实施其中一个之前,我想问一下为什么会发生这样的错误。我问的原因是因为我无法复制这个错误,所以我正在实施一个故障恢复解决方案。

我得到的错误是在一个TList对象上:“ [EListError] List index out of bounds (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 项,死引用应该触发访问冲突错误,不是吗?

4

2 回答 2

3

三种可能的原因:

  • 内存覆盖,恰好覆盖“可访问”内存中的内容(因此没有 AV)。但会触发对 的无效更改FormListObject

  • 其他的东西(比如 OnShow 事件)实际上是从FormListObject. 它不必是不同的线程;但当然这也是可能的。注意:尽管您询问这是否是 GUI 线程问题,但我真的怀疑是这种情况,因为您必须专门从不同的线程调用 GUI 代码才能实现这种可能性。

  • 可重入代码导致FormListObject在循环中间发生更改。当您Application.ProcessMessages在处理另一条消息的过程中调用时会发生这种情况。我的调试器感觉在这个上抽搐。即使您没有犯罪Application.ProcessMessages,您所展示的表格上的某些组件也可能是。

PS:我在评论中注意到你说你已经有一个堆栈跟踪。进一步检查堆栈跟踪,看看您的代码是否可重入。


由于您无法重现该问题,因此您需要额外的调试日志记录以获取更多信息。假设您有简单地附加到调试文件的 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

编辑

我添加了第三种可能性;我实际上有一种预感是最可能的原因。
还添加了一些具体的调试建议来缩小范围。

于 2014-05-02T08:04:44.933 回答
3

我得到的错误是在 TList 对象上:“[EListError] List index out of bounds (0)”。

这很容易理解。您有一个空列表并试图访问索引为零的元素。

使用调试器告诉您哪个列表是空的。让调试器中断异常。然后尝试找出为什么您的代码假定列表不是空的,而事实并非如此。

于 2014-05-01T16:51:53.923 回答