尝试/捕获似乎无法捕获异常 - Delphi服务应用程序

4

我有一个使用Delphi 2007编写的服务,我正在尝试捕获任何未知异常。将方法分配给on exception似乎不起作用('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'从未被调用,应用程序继续运行(我认为它正在等待某人在创建的隐形错误对话框上单击'确定')。有什么想法为什么"except"下面的代码不会被调用吗?谢谢!KP

3
由于您的 except 捕获并处理了异常,因此它永远不会触发 Application.OnException 事件。除了 HaltExitCode := -1 也被跳过了吗?如果是这样,那么问题就在于 LogEvent 中,您应该发布一些代码来展示 LogEvent 的操作。 - Jeroen Wiert Pluimers
他谈论了一个对话框,但是在这个上下文中,他传递了一个事件日志参数(EVENTLOG_AUDIT_FAILURE),这是错误的。我记得它用于写入安全日志,但自从XP SP2以来,它不再起作用(使用AuthzReportSecurityEvent)。可能他应该使用类似EVENTLOG_ERROR_TYPE的东西,但如果没有LogEvent代码,很难说。 - user160694
4个回答

6
重新分配Forms.Application.OnException是一个不好的主意,因为TServiceApplication.Run()会自己处理异常。你可以在它之前分配,但是你的分配将没有效果,或者你可以在它之后分配,这样就会移除已经放置的异常处理机制。
如果保留处理机制,那么所有异常都将被记录到Windows事件日志中,从服务的角度来看,这似乎是个合理的做法。

谢谢你,我已经删除了 'Forms.Application.OnException:=UnknownApplicationException' - 没有这个东西,我可以看到异常现在被自动记录到Windows事件查看器中 - 我仍然希望能够通过编程终止服务 - 有什么办法吗?停止似乎不起作用,并且正如其他人指出的那样,这并不是最好的方法,谢谢。 - Kunal
1
@Kunal:您可以通过请求新状态来停止服务,但是如果您想获得详细的解释,最好在Stack Overflow上提出一个新问题。其他人可以从更专注的问答中受益。 - mghie
TServiceApplication和TService都进行内部异常处理,记录未捕获的异常到Windows事件日志中。因此,即使异常发生,也很少会触发OnException事件。但是,如果您想要对未捕获的异常做出反应,那么可以从TServiceApplication派生一个新类,并覆盖其DoHandleException()方法。 - Remy Lebeau
1
@Remy:我不明白这怎么可能行得通,因为TServiceApplication.Create(nil)是在SvcMgr.pasinitialization期间执行的。钩住TServiceApplication.DoHandleException()似乎是唯一可行的解决方案,但这是一个实现细节,可能会在以后的VCL版本中更改。 - mghie
1
你可以释放默认的TServiceApplication对象,并用自己的对象替换它,例如:Application.Free; Application := TMyServiceApplication.Create(nil); - Remy Lebeau

2

几点注意事项:

  • 由于你在try-except块内引发了异常,所以它不应触发任何Application.OnException处理程序,因为该异常未被处理。

  • 你是如何确定Halt没有被调用的?异常是否通过LogEvent记录下来了?

  • 在服务应用程序中,ExitCode和Halt的功能与普通Windows应用程序的预期方式不同。 通过调用halt无法停止服务,而应通过Windows的服务控制管理器来停止服务。

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

  • 服务通常不会有与之相关联的桌面,因此显示对话框是行不通的。

  • 如果需要服务显示对话框(顺便说一句,这是个不好的想法,服务旨在在没有用户交互的情况下运行),则需要使其交互,并在除了服务运行的正常“系统”帐户之外的另一个用户帐户下运行。你可以通过服务管理器来实现这一点。


关闭:"服务通常没有与之关联的桌面,因此显示对话框是行不通的。" - 行不通,你可能需要重新启动系统,因为服务会被卡在尝试显示该窗口的过程中。非常糟糕的想法。为什么不写入文本文件或发送电子邮件来查看是否捕获异常,而不使用logevent函数呢?此致, Radu - RBA

1
为什么要设置 Forms.Application?据我所知,服务使用在 SvcMgr 中声明的 Application 变量,其声明如下:
var
  Application: TServiceApplication = nil;

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

0

我创建了自己的版本的SvcMgr.pas文件,以消除对应用程序全局异常处理程序的就地钩子,以便我可以实例化自己的异常处理程序。我这样做是因为1)我找不到其他简单的方法来做到这一点,2)由于该单元是一个独立的单元,仅与Windows服务一起使用,因此对其他单元的影响很小。您可以从我的网站下载代码以了解其工作原理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接