You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Delphi 12应用程序级捕获所有异常并定位报错位置?

全局捕获Delphi异常并定位代码行的方案

针对你使用Delphi 12 + TIdTCPServer/TThreadProcess组件遇到的偶发EMonitorLockException(提示"unlock list object lock not owned")且无法定位代码行的问题,可通过以下方式全局处理异常并获取调用栈信息:

一、全局异常捕获机制

1. 主线程与VCL线程异常

通过Application.OnException事件捕获主线程及VCL相关线程的异常,在程序启动阶段注册该事件:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnException := HandleGlobalException;
end;

procedure TMainForm.HandleGlobalException(Sender: TObject; E: Exception);
var
  CallStack: string;
begin
  // 获取异常调用栈
  CallStack := ExceptionStack(E);
  // 输出异常详情(可替换为日志写入逻辑)
  ShowMessageFmt('异常信息: %s'#13#10'调用栈: %s', [E.Message, CallStack]);
end;

2. 自定义线程异常(含Indy线程、TThreadProcess)

线程内的异常默认不会触发Application.OnException,需在线程执行逻辑外层包裹try...except捕获,或重写线程的Execute方法:

type
  TMyIndyWorkerThread = class(TIdThreadWithTask)
  protected
    procedure Execute; override;
  end;

procedure TMyIndyWorkerThread.Execute;
var
  CallStack: string;
begin
  try
    inherited;
    // 你的线程业务逻辑
  except
    on E: Exception do
    begin
      CallStack := ExceptionStack(E);
      // 同步到主线程显示或写入日志
      TThread.Synchronize(nil, procedure
      begin
        ShowMessageFmt('线程[%d]异常: %s'#13#10'调用栈: %s', 
          [ThreadID, E.Message, CallStack]);
      end);
    end;
  end;
end;

对于TThreadProcess,同样在其执行代码外层添加try...except块,捕获后获取调用栈信息。

二、获取精确调用栈(含代码行号)

要获取代码行号,需确保编译时开启调试信息:

  1. 打开项目选项(Project Options)
  2. 进入Building > Delphi Compiler > Linking
  3. 勾选Include remote debug symbols和Include TD32 debug info(Debug配置默认已开启)

Delphi 12原生提供ExceptionStack函数(位于System.SysUtils单元),可直接获取包含过程名、代码行号的调用栈。如需更定制化的信息,可使用如下辅助函数:

function GetFullExceptionStack(E: Exception): string;
var
  StackInfoPtr: Pointer;
begin
  Result := ExceptionStack(E);
  StackInfoPtr := E.StackTrace;
  if Assigned(StackInfoPtr) then
    Result := StackInfoToString(StackInfoPtr);
end;

三、针对EMonitorLockException的专项排查

该异常属于线程同步错误,通常是解锁了未被当前线程持有的锁,建议:

  • 检查所有同步对象(TCriticalSection、Indy的TIdThreadSafe系列组件等)的使用,确保锁的获取与解锁严格配对,异常路径下不要遗漏解锁操作。
  • 开启线程安全检查:在项目选项Building > Delphi Compiler > Compiling中勾选Enable thread safety checks,调试时可提前发现锁操作违规。
  • 为EMonitorLockException添加异常断点:调试时通过Debug > Add Breakpoint > Exception Breakpoint添加该异常,触发时直接中断到抛出点。

四、调试辅助建议

  • 用日志记录替代弹窗:弹窗会干扰线程执行顺序,增加复现难度,建议将异常信息写入日志文件,包含时间戳、线程ID、调用栈。
  • 复现问题时尽量保持环境稳定:减少调试时的额外操作,避免因线程调度变化导致异常消失。

内容的提问来源于stack exchange,提问作者Robe79

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:38:21