【问题标题】:try/except doesn't seem to capture exceptions - Delphi Service Applicationtry/except 似乎没有捕获异常 - Delphi Service Application
【发布时间】:2010-07-05 05:41:47
【问题描述】:

我有一个用 Delphi 2007 编写的服务,我正在尝试捕获任何未知异常。为 on 异常分配方法似乎不起作用('Forms.Application.OnException:=UnknownApplicationException')。 'UnknownApplicationException' 似乎没有被调用——我将此归因于应用程序中没有表单,因此该方法实际上从未被分配。除此之外,我还尝试在计时器上创建一个异常(在注释掉“Forms.Application.OnException:=UnknownApplicationException”以便它不会干扰之后)。定时器在服务启动 60 秒后触发:

procedure TProcessScheduler.Timer1Timer(Sender: TObject);    
begin    
  try    
    Raise Exception.Create('THIS GIG SUCKS');    
  except     
    LogEvent(Name,rsUNKNOWN_EXCEPTION,EVENTLOG_AUDIT_FAILURE,0);    
    ExitCode:=-1;    
    Halt;    
  end;     
end;

该异常似乎从未被捕获 - 服务启动,并且在此计时器触发 60 秒后,我听到 Windows 错误声音但没有看到任何错误对话框 - 这可能是由于应用程序是服务吗? 'Halt' 永远不会被调用并且应用程序继续运行(我假设它正在等待某人在它创建的不可见错误对话框上单击确定)。任何想法为什么不调用“除外”下的代码?提前致谢! KP

【问题讨论】:

  • 由于您的except 捕获并处理异常,它永远不会到达Application.OnException 事件。除了Halt 之外ExitCode := -1 是否也被跳过了?如果是这样,那么问题出在LogEvent 内部,您应该发布一些代码来显示LogEvent 的作用。
  • 他谈到了一个对话框,但他传递了一个事件日志参数 (EVENTLOG_AUDIT_FAILURE),在这种情况下这是错误的,用于写入安全日志的 IIRC,但从 XP SP2 开始不再起作用(使用 AuthzReportSecurityEvent)。可能他应该使用类似 EVENTLOG_ERROR_TYPE 的东西,但是没有 LogEvent 代码就很难说。

标签: delphi exception-handling delphi-2007


【解决方案1】:

重新分配Forms.Application.OnException 是个坏主意,因为TServiceApplication.Run() 本身就是这样做的。你要么在之前做,那么你的分配不会有效果,或者你在之后做,在这种情况下,你删除了已经到位的异常处理机制。

如果您将处理留在原地,那么所有异常都将记录到 Windows 事件记录器中,这对于服务来说似乎是合理的。

【讨论】:

  • 感谢您,我删除了 'Forms.Application.OnException:=UnknownApplicationException' - 没有这个,我可以看到异常现在自动记录到 Windows 事件查看器中 - 我仍然会喜欢能够以编程方式终止服务 - 知道如何做到这一点吗?停止似乎不起作用,正如其他人指出的那样,这并不是最好的方法,谢谢
  • @Kunal:您可以通过请求新状态来停止服务,但要获得详细解释,您最好在 Stack Overflow 上提出一个新问题。然后其他人可以从更集中的 Q 和 A 中受益。
  • TServiceApplication 和 TService 都进行内部异常处理,将未捕获的异常记录到 Windows 事件日志中。因此,异常很少会到达 OnException 事件。但是,如果您想对未捕获的异常做出反应,请尝试从 TServiceApplication 驱动一个新类并覆盖其 DoHandleException() 方法。
  • @Remy:我看不出这是怎么工作的,因为TServiceApplication.Create(nil) 是在 SvcMgr.pasinitialization 期间执行的。挂钩 TServiceApplication.DoHandleException() 看起来是唯一可行的解​​决方案,但这是一个实现细节,可能会在以后的 VCL 版本中发生变化。
  • 你可以释放默认的TServiceApplication对象,替换成你自己的,即:Application.Free;应用程序:= TMyServiceApplication.Create(nil);
【解决方案2】:

几点说明:

  • 当您在 try-except 块中引发异常时,它不应触发任何 Application.OnException 处理程序,因为该异常未被处理。

  • 您如何确定没有调用 Halt?异常是否通过您的 LogEvent 记录?

  • 在服务应用程序中,ExitCode 和 Halt 的功能与您在普通 Windows 应用程序中的预期不同。服务不会通过调用 halt 停止,它应该通过 Windows 的服务控制管理器停止。

  • 如果您的 try-except 块的 except 部分确实没有到达,这意味着 Windows 已经介入,因为发生了一些它不满意的事情。这可能是您正在调用的 LogEvent 方法中的内容。如果这显示了一个对话框,或者如果这也引发了异常,则不会到达 ExitCode 和 Halt。

  • 服务通常没有与之关联的桌面,因此无法显示对话框。

  • 如果您需要该服务来显示对话框(顺便说一下,这是个坏主意,服务的目的是在没有用户交互的情况下运行),您需要使其具有交互性并让它在另一个用户帐户下运行,而不是正常的“系统” " 服务运行的帐户。您可以通过服务管理器执行此操作。

【讨论】:

  • off: "一个服务通常没有与之关联的桌面,所以显示对话框是行不通的。"- 行不通,你可能需要重新启动系统,因为服务在试图显示该窗口时卡住了。非常糟糕的主意。为什么不使用 logevent 函数,而不是写入文本文件或发送电子邮件以查看异常是否被捕获?最好的问候,拉杜
【解决方案3】:

为什么要设置 Forms.Application? AFAIK 服务使用在 SvcMgr 中声明的 Application 变量,声明为:

var
  Application: TServiceApplication = nil;

此外,服务不应显示任何对话框,它可能无法访问用户桌面,并且您的对话框将挂起该服务。无论如何都有显示对话框的方法,但服务也可以在没有人类用户观看屏幕时运行。 将事件记录到事件日志中(或者如果您不喜欢它到文件中,但事件日志有几个有用的功能,包括远程访问)。

【讨论】:

    【解决方案4】:

    我创建了我自己的 SvcMgr.pas 文件版本,以消除应用程序全局异常处理程序的就地挂钩,以便我可以实例化我自己的。我这样做是因为 1) 我找不到其他简单的方法,并且 2) 由于该单元是仅包含在 Windows 服务中的独立单元,因此对其他单元的影响很小。您可以从我的网站下载代码,看看它是如何工作的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 2011-06-26
      相关资源
      最近更新 更多