繁体   English   中英

如何识别在Delphi应用程序中最终确定期间抛出异常的位置?

[英]How to identify where an exception is being thrown during finalization in a Delphi application?

我正在与客户进行现场工作,我正在努力帮助他们解决一个复杂的问题。 我希望Delphi中有一个工具或功能,我们可以用它来查看内部工作,以帮助我们找到问题所在。

以下是我们正在处理的问题的高级概述。 这是一个商业应用程序,目前部署在Delphi 5中。在过去的一年中,该应用程序已迁移到Delphi XE。 迁移几乎完成,但遇到了一些严重的错误。

应用程序本身非常庞大,有数百个单元和许多第三方和自定义组件。 在我们遇到的一个特定情况中,创建主窗体,然后在显示主窗体之前终止应用程序。 结果是在终止期间发生崩溃,因为单元正在最终确定。

调试器正在破坏kernel32的RaiseException函数,该函数由NotifyNonDelphiException调用。 我们尝试设置一个非破坏断点,从NotifyNonDelphiException中记录调用堆栈,但这并没有给我们任何有用的东西。 调用堆栈仅包含处理异常的方法,即RtlRaiseStatus和KUserExceptionDispatcher。

我们如何识别抛出NotifyNonDelphiException正在处理的原始异常的代码?


编辑:这是在一个异常实例后捕获的两个图像。 第一个是引发异常,第二个描述异常对话框关闭后的CPU窗口。

退出时访问冲突

关闭异常对话框时的CPU窗口

新编辑:

我发布这个问题已经有一个多星期了,各种答案给我留下了深刻的印象。 对最初问题的一些评论是最有价值的,但是一些答案本身非常有用。

我对该客户的访问已经结束,我将要求他们考虑已在此处发布的答案。 虽然我们无法追踪错误的实际来源,但错误的原因显而易见。 多年来在没有严重重构的情况下对用户界面进行调整,使应用程序的登录过程不稳定。 当用户取消登录时,主表单处于部分初始化状态。 当不允许此过程运行时,这是用户中止登录时发生的情况,存在非常严重的终结问题。

该公司购买了AQTime Pro以帮助识别未来的问题,但需要重新设计登录过程,并且从长远来看将解决问题。

有一次,我考虑删除这个问题,但我选择保留它,因为我相信其他人会发现许多优秀的建议,这些建议已经发布了信息。

目前,我接受了@Deltics的答案,因为我讨厌在没有答案的情况下留下问题。 但是,我要求这个问题的观众也考虑所有其他答案和评论,它们同样有价值。

出于这个原因,永远不应允许异常从“ 终止” (或初始化 )部分“逃避”。

除了极少数例外[原文如此], 终结部分中的任何代码都应该包含在try..except中 当你得到一个异常时你所做的事情取决于你,但至少调用OutputDebugString()会在调试时给你提供信息,并给你一个设置一个断点的点,这个断点只会导致一个断点发生了异常。

finalization
  try
    // Perform finalization processing here

  except
    on e: Exception do
      OutputDebugString('%s: $s in unit %s', [e.ClassName, e.Message, 'MyUnitName']);
  end;
end.

注意:此代码中的OutputDebugString()调用是我自己的“字符串友好”,包装在Windows单元中的函数,扩展为接受args。

由于您可能在最终确定部分中没有此类异常处理,因此这将涉及在您继续之前将它们放置到位。 但是,这个练习将提高整体代码的质量,并且在将来更容易诊断任何类似的问题(谁说,一旦你确定并修复了当前的异常,其他一些终结异常不会引起它的丑陋头?)。

此外,应用此异常处理的过程将使您有机会查看每个最终确定部分,并确定是否可以不同地处理它,以便尽可能多地消除最终化部分。

这不应被视为“不必要的开销”或“浪费时间”,而是将您的代码质量提升到可接受标准的重要内务管理。

另一种方法

另一种方法是管理您自己的完成程序列表,正如我们在引入最终部分之前必须做的那样。 即,在当前已完成的单元的初始化部分中,将当前的最终化代码移除到无参数过程中,并使用“终结管理器”注册该过程。

然后,在您的应用程序中,在应用程序关闭期间的适当时间调用“终结管理器”,以任何实际单元终结之前执行最终确定。 这可确保在运行时异常处理程序仍然存在的情况下执行完成过程。

这也为提供更复杂的“最终确定管理器”提供了机会,其机制可确保以特定的,确定的顺序(如果需要)执行完成程序。 这种能力自然取决于您如何实现自己的终结管理器。

  1. 转到查看/调试Windows /模块,找到cxLibraryD15.bpl并提取其基本地址。 现在,减去$ 00E51B9E - base = offset。

  2. 运行您的应用程序并立即暂停它。 转到查看/调试Windows /模块,找到cxLibraryD15.bpl并提取其基本地址(它可能是相同的)。 现在,将步骤1中的偏移量添加到它:base + offset =绝对地址。

  3. 打开断点窗口或CPU视图并在步骤2的地址处设置断点。现在您将在异常发生之前停止,这样您就可以看到调用堆栈并分析调试器中的情况。

尝试将断点设置为KiUserExceptionDispatcher,RaiseException和Exception.GetExceptionStackInfoProc。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM