4

我正在与客户一起在现场工作,并试图帮助他们解决一个复杂的问题。我希望 Delphi 中有一个工具或功能,我们可以使用它来查看内部工作原理,以帮助我们定位问题。

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

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

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

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


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

退出时访问冲突

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

新编辑:

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

我对那个客户的访问已经结束,我会要求他们考虑这里发布的答案。虽然我们无法追踪错误的实际来源,但错误的原因非常明显。多年对用户界面进行调整而没有进行认真的重构,导致应用程序的登录过程不稳定。当用户取消登录时,主窗体处于部分初始化状态。当该进程不允许运行时,即用户中止登录时发生的情况,就会出现非常严重的最终确定问题。

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

有一次我考虑删除这个问题,但我选择将其发布,因为我相信其他人会发现发布的许多优秀建议提供了丰富的信息。

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

4

3 回答 3

5

正是出于这个原因,不应允许异常从finalization(或initialization )部分“逃脱”。

除了极少数例外 [原文如此],终结部分中的任何代码都应包含在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。

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

此外,应用此异常处理的过程将使您有机会审查每个终结部分并确定是否不能对其进行不同的处理,以期消除尽可能多的终结部分。

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

另一种方法

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

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

这也提供了提供更复杂的“终结管理器”的机会,具有确保终结过程以特定的、确定的顺序(如果需要)执行的机制。此功能自然取决于您如何实现自己的终结管理器。

于 2012-03-06T21:37:02.607 回答
2
  1. 转到 View/Debug Windows/Modules,找到 cxLibraryD15.bpl 并提取其基地址。现在,减去 $00E51B9E - base = offset。

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

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

于 2012-03-08T04:50:09.273 回答
0

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

于 2012-03-07T08:47:16.347 回答