【问题标题】:Occasional EAccessViolation in VCL/comctl32.dll/USER32.dll/GDI32.dll after receiving WM_PAINT收到 WM_PAINT 后,VCL/comctl32.dll/USER32.dll/GDI32.dll 中偶尔出现 EAccessViolation
【发布时间】:2013-05-25 19:58:22
【问题描述】:

我需要一些关于调试 Delphi XE2 应用程序崩溃的建议。我自己从来没有见过崩溃——事实上它很少发生并且不能按需重现。

我们确实有一组来自 MadExcept 的 10 份崩溃报告。这些表明主线程当时正在主窗体的列表视图中处理 WM_PAINT 消息。每种情况下的调用堆栈都没有显示对我自己代码的引用,只有 VCL 代码和 comctl32.dll、ntdll.dll 和 USER32.dll 中的函数。

有问题的列表视图是 TColorListView,它派生自 TCustomListView,并处理 OnCustomDrawItem 和 OnDeletion 事件。但正如我所说,当崩溃发生时,我的 TColorListView 代码都不在调用堆栈上。

每种情况下崩溃的实际位置各不相同,但导致崩溃的调用顺序(从早到晚)始终是:

KiUserCallbackDispatcher
RtlAnsiStringToUnicodeString
StdWndProc
TWinControl.MainWndProc
TCustomListView.WndProc
TWinControl.WndProc
TControl.WndProc
TCustomListView.WMPaint
TWinControl.WMPaint
TWinControl.WMPaint
TWinControl.DefaultHandler
CallWindowProcA
TControl.WndProc

之后它进入 StdWndProc/SendMessageW/TControl.Perform 之一,从那里每次路径都不同。最终它以 comctl32.dll、USER32.dll、GDI32.dll 或 TControl.WndProc 之一结束,并引发 EAccessViolation。遗憾的是,我没有关于用户当时尝试做什么的信息,因为用户没有填写错误报告的那部分。

您能否建议我可以使用任何“心理调试”技术来尝试确定这次崩溃的原因(并因此修复它)?


更新以回答以下 cmets 中的问题:

procedure TColorListView.HandleCustomDrawItem(aSender: TCustomListView; aItem: TListItem;
                                              aState: TCustomDrawState; var aDefaultDraw: Boolean);
begin
  Canvas.Font.Color := ItemColors[aItem.Index];
end;

在(仅)一个崩溃报告中,它似乎进入了 TListItem.GetIndex 并进一步崩溃了几个堆栈帧。不过,这可能是一个红鲱鱼。

“已执行”消息是什么?对不起,我不知道。 MadExcept 没有给我方法参数值;只是方法名称。


5 月 31 日

虽然我更愿意仅从我拥有的信息中找出故障,但我也欢迎对我可以添加到程序中的任何新诊断提出建议,这样如果在下一个版本之后再次发生这种崩溃,我将有更多继续。不过我很茫然,因为在崩溃时,我可以修改的代码都没有在调用堆栈上。


6 月 13 日

我在 MadExcept 报告中添加了一行,告诉我异常发生时应用程序处于什么状态 - 启动/活动/空闲/ModalDlg/终止。 (感谢 Chris Thornton 的评论建议这一点。)我认为在关机期间发生异常的可能性是合理的。不幸的是,我们要到 2014 年才会发布新版本,并有可能通过新诊断获取错误报告。

【问题讨论】:

  • OnCustomDrawItem 会发生什么?
  • “执行”的消息是什么?
  • 查看上面添加的答案。
  • 当您的应用程序启动并运行,等待输入时是否会发生这种情况?还是在关机或从待机状态恢复等“边缘情况”期间发生?
  • @Chris 我希望我知道。我拥有的唯一信息是 MadExcept 报告。我曾想到可能是应用程序关闭时,但这只是猜测。

标签: delphi debugging delphi-xe2 postmortem-debugging wm-paint


【解决方案1】:

在这里阅读很有趣。也许类似的事情正在发生? 检查画布 nil

不会有什么坏处

Access violation while the program was idle - not trace information to track down the bug

【讨论】:

  • HandleCustomDrawItem 是我唯一使用 Canvas 的地方,而这不是它崩溃的地方。只有一个崩溃堆栈转储提到了 TColorListView。
【解决方案2】:

首先,要调试访问冲突错误,您必须找到引用进程不拥有内存的区域的变量(内存指针)。

大多数未初始化的变量会导致问题。

所以我的建议是按以下方式更改作品

procedure TColorListView.HandleCustomDrawItem(aSender: TCustomListView; aItem: TListItem;
                                          aState: TCustomDrawState; var aDefaultDraw:    boolean);
begin
   if Canvas = nil then 
       .... ; // a breakpoint here
   if ItemColors = nil then 
       .... ; // a breakpoint here
   if aItem = nil then 
       .... ; // a breakpoint here
   Canvas.Font.Color := ItemColors[aItem.Index];
end;

我希望这会告诉你哪个变量没有按预期传递。

我的猜测是针对 aItem。

【讨论】:

  • 查看我对 Chris Thornton 的回答的评论。发生访问冲突的不是这种方法,而且在任何情况下,该错误都无法重现,因此设置断点无济于事。
【解决方案3】:

这只是一个猜测,但也许您面临与我相同的问题(看起来很相似)。
我的问题在于在不同线程中销毁 WinAPI 窗口,而不是创建它们。
在这种情况下,Windows 不会破坏窗口并返回错误,但某些 Delphi 组件会忽略该错误,因此您最终会得到 WndProc 指向垃圾内存的挂起窗口(它将由 Delphi 在组件销毁,但窗口将留在后面)。
当此窗口尝试处理任何消息时,它将转到 WndProc(未定义)并导致带有 AV 的随机调用堆栈。

所以确保你在同一个线程中创建和删除窗口(特别注意TTimer,他们也会创建窗口)

【讨论】:

    猜你喜欢
    • 2012-03-23
    • 2013-01-21
    • 2014-02-12
    • 2012-01-21
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    相关资源
    最近更新 更多