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

Delphi TService在Win10及Server 2012R2上的周期性异常排查求助

针对Delphi TService未处理异常的排查方案

我之前也维护过类似的Delphi TService多线程项目,这种找不到根源的未处理异常确实头疼,结合你的场景,给你几个针对性的排查方向:

  • 先给服务加上全局异常捕获
    默认的TService可能不会把线程内的异常主动抛到调试器,你可以在项目的DPR文件或者TService.OnCreate事件里添加全局异常处理逻辑,把所有异常(包括线程致命异常)写入日志,重点记录调用栈、线程ID、时间戳:

    procedure GlobalAppException(Sender: TObject; E: Exception);
    begin
      TFile.AppendAllText('ServiceCrash.log', 
        FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', Now) + 
        ' | Main Thread Exception: ' + E.Message + #13#10 + 
        'Call Stack: ' + E.StackTrace + #13#10#13#10);
    end;
    
    procedure ThreadTerminateHandler(Sender: TObject);
    var
      CurrThread: TThread;
      FatalEx: Exception;
    begin
      CurrThread := Sender as TThread;
      if Assigned(CurrThread.FatalException) then
      begin
        FatalEx := Exception(CurrThread.FatalException);
        TFile.AppendAllText('ServiceCrash.log', 
          FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', Now) + 
          ' | Thread [' + CurrThread.Name + '] Fatal Exception: ' + FatalEx.Message + #13#10 + 
          'Call Stack: ' + FatalEx.StackTrace + #13#10#13#10);
      end;
    end;
    
    // 在DPR文件中注册
    begin
      Application.Initialize;
      Application.OnException := GlobalAppException;
      TThread.OnTerminate := ThreadTerminateHandler;
      Application.CreateForm(TYourService, YourService);
      Application.Run;
    end.
    

    日志里的调用栈信息是定位问题的核心,能直接帮你找到异常触发的代码行。

  • 重点排查轮询线程的资源冲突
    2-300ms的高频轮询很容易踩线程安全的坑:

    • 检查设备通信句柄/连接是否被多个线程共享,有没有用TCriticalSection或者TMutex做资源保护?
    • 数据库访问部分,每个线程必须用独立的连接实例(比如每个线程自己创建TADOConnection),绝对不能共享同一个连接对象;如果用连接池,要确认池的实现是线程安全的。
    • 轮询逻辑里的全局变量(比如统计轮询次数、设备状态的变量),必须用InterlockedIncrement/InterlockedExchange这类原子操作修改,避免多线程读写冲突。
  • 换一种调试方式:Attach到运行中的服务进程
    直接用调试器启动TService经常会跳过一些异常触发点,你可以先手动启动服务,然后在Delphi里选择Run > Attach to Process,找到你的服务进程(编译后是exe文件),Attach成功后再等待异常发生,调试器大概率能停在异常触发的代码位置。
    记得提前开启调试选项里的「Stop on All Exceptions」,不要只勾选Delphi自带的Exception类型。

  • 隔离定时任务与轮询逻辑
    每日执行的特定操作可能和轮询线程存在资源竞争,比如定时任务需要独占设备或数据库锁,而轮询线程还在持续访问。可以临时注释掉定时任务的代码,观察异常是否还会出现,以此缩小排查范围。

  • 检查设备通信的异常边界
    轮询设备时,务必处理所有异常场景:设备突然离线、返回数据包不完整、通信超时等。很多时候外部设备的异常如果没被捕获,会直接导致线程抛出未处理异常。比如在串口/网络通信的代码块外层加上try...except,哪怕只是记录日志也比让线程崩溃好。

  • 查看Windows系统事件日志
    如果服务直接崩溃退出,Windows事件查看器的「应用程序日志」里会记录崩溃的详细信息,包括异常代码、出错的模块名称,这些信息能帮你快速定位是不是第三方控件DLL或者系统API调用出了问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:41:29