Delphi应用陷入Access Violation无限循环,如何用madExcept捕获追踪?
Delphi应用概率性Access Violation无限循环异常排查
问题现象
- 应用概率性出现停滞,陷入Access Violation异常无限循环,异常对话框反复弹出
- 开启madExcept编译后,Bug报告进度对话框反复弹出,无法查看单份报告
- 异常在
DataGridHotTrackNode事件中触发,前10-20秒代码运行正常
已尝试的madExcept配置(无效)
- 勾选「异常自动操作-各类操作-自动保存Bug报告(仅当报告未发送时)」
- 勾选「异常自动操作-应用控制-自动继续应用」
- 未找到自动保存的Bug报告,应用仍持续运行并触发异常循环
相关触发代码
procedure TInvoiceForm.DataGridHotTrackNode(Sender: TObject; AHotTrackInfo: TdxTreeListHotTrackInfo; var ACursor: TCursor); var HitInfo: TdxTreeListHitInfo; GridPoint: TPoint; AColumn: TdxDBTreeListColumn; ARow: Integer; Tmp: String; begin if IsPopupShowing then Exit; IsPopupShowing := True; try GridPoint := DataGrid.ScreenToClient(Mouse.CursorPos); HitInfo:=DataGrid.GetHitInfo(GridPoint); if ( (HitInfo.hitType = htColumn) or (HitInfo.hitType = htLabel) ) then begin if Assigned(HitInfo.Node) then if {Assigned(HitInfo.Node.Values[HitInfo.Column])} (1=1) then begin try if HitInfo.Column<HitInfo.Node.CountValues then if not VarIsNull(HitInfo.Node.Values[HitInfo.Column]) then Tmp:=HitInfo.Node.Values[HitInfo.Column] else Tmp:='' else Tmp:=''; except on E: Exception do begin Tmp:=''; end; end; if PopupForm <> nil then FreeAndNil(PopupForm); PopupForm := TForm.Create(nil); try // Configure PopupForm based on the Node information // ... if Assigned(PopupForm) then PopupForm.Caption:=Tmp; if Assigned(PopupForm) then PopupForm.Position := poDesigned; if Assigned(PopupForm) then PopupForm.Left := Mouse.CursorPos.X; // Adjust as needed if Assigned(PopupForm) then PopupForm.Top := Mouse.CursorPos.Y; // Adjust as needed if Assigned(PopupForm) then PopupForm.Show; except FreeAndNil(PopupForm); end; end; end else begin if Assigned(PopupForm) then FreeAndNil(PopupForm); end; finally IsPopupShowing := False; end; end;
注:正在为TdxDBGrid单元格开发自动弹出/关闭的窗口,已添加单线程并发防护
解决方案建议
一、madExcept配置优化
- 强制保存Bug报告:
- 打开madExcept设置,进入「异常自动操作」→「各类操作」,勾选「自动保存Bug报告(总是保存,不管是否发送)」
- 手动指定绝对保存路径(如
C:\BugReports\),确保程序拥有该路径的读写权限 - 取消「自动继续应用」,改为勾选「自动终止应用(仅当报告已保存时)」,避免异常循环触发
- 屏蔽重复异常弹窗:
进入「异常处理」→「高级」,勾选「忽略重复的异常(短时间内同一位置触发的相同异常)」,设置500ms左右的阈值 - 启用静默异常处理:
在程序启动代码中添加自定义异常处理,直接保存报告不弹窗:procedure SaveExceptionSilently(const exceptIntf: IMEException; var handled: Boolean); begin exceptIntf.SaveBugReportToFile('C:\BugReports\report_' + DateTimeToStr(Now) + '.zip'); handled := True; end; // 在FormCreate或项目初始化中注册 madExcept.RegisterExceptionHandler(SaveExceptionSilently, stDontSync);
二、代码风险点修复
- 优化PopupForm生命周期:
避免反复创建销毁非模态窗口,改用全局实例复用:// 全局变量改为:PopupForm: TForm = nil; if not Assigned(PopupForm) then PopupForm := TForm.Create(nil); // 配置PopupForm... PopupForm.Show; // 需要关闭时再FreeAndNil(PopupForm) - 增强HitInfo有效性校验:
添加HitInfo.Column的空值检查,避免无效索引访问:if Assigned(HitInfo.Node) and Assigned(HitInfo.Column) then begin if HitInfo.Column.Index < HitInfo.Node.CountValues then // 访问Values逻辑 end; - 强化竞态防护:
用TCriticalSection保护IsPopupShowing变量,避免事件重入触发:// 声明全局临界区:FCriticalSection: TCriticalSection; // 初始化:FCriticalSection := TCriticalSection.Create; // 销毁:FCriticalSection.Free; FCriticalSection.Enter; try if IsPopupShowing then Exit; IsPopupShowing := True; finally FCriticalSection.Leave; end; - 缓存鼠标坐标:
在事件开头缓存坐标,避免后续处理中坐标变化导致的异常:GridPoint := DataGrid.ScreenToClient(Mouse.CursorPos); const MousePos = Mouse.CursorPos; // 缓存坐标 // 后续设置PopupForm位置时用MousePos PopupForm.Left := MousePos.X; PopupForm.Top := MousePos.Y;
三、其他排查手段
- WinDbg附加分析:当异常循环时,用WinDbg附加到进程,执行
!analyze -v获取详细异常调用栈,定位具体出错代码行 - 添加日志追踪:在事件关键节点(创建PopupForm、访问Node.Values、释放窗口)添加日志,记录时间、变量值,回溯异常触发前状态
- 临时禁用事件:注释掉
DataGridHotTrackNode事件绑定,确认异常是否消失,验证是否为该事件导致
内容的提问来源于stack exchange,提问作者TomR
相关产品推荐
相关产品推荐

