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

时间:2012-03-06 19:35:46

标签: delphi debugging delphi-xe kernel32

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

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

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

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

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


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

Access violation upon exit

CPU window upon closing the exception dialog box

新编辑:

自我发布这个问题已经过去一周多了,我对各种答案印象深刻。对初始问题的一些评论是最有价值的,但是一些答案本身就非常有用。

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

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

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

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

3 个答案:

答案 0 :(得分:5)

出于这个原因,绝不允许例外情况从“终结(或初始化)”部分“逃避”。

除了极少数例外情况[原文如此],终结部分中的任何代码都应该包含在 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 <中的函数/ strong> unit,扩展为接受args。

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

此外,应用此异常处理的过程将使您有机会审核每个最终确定部分,并确定是否无法以不同方式处理,以便消除尽可能多的最终确定< / strong>部分尽可能。

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

另类方法

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

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

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

答案 1 :(得分:2)

  1. 转到View / Debug Windows / Modules,找到cxLibraryD15.bpl并解压缩其基本地址。现在,减去$ 00E51B9E - base = offset。

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

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

    < / LI>

答案 2 :(得分:0)

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