如何调试仅在应用程序关闭时发生的崩溃? (德尔福)
Posted
技术标签:
【中文标题】如何调试仅在应用程序关闭时发生的崩溃? (德尔福)【英文标题】:How to debug a crash that only occurs on application shutdown? (Delphi) 【发布时间】:2011-07-08 01:22:20 【问题描述】:因此,经过最近的一些更改,我们发现我们最古老的应用程序之一有时会在关机时崩溃。这表现为“运行时错误 216”消息或来自 Windows 错误报告的应用程序已停止工作的消息。该应用程序已经在每次发送OutputDebugString
-messages 并且 AFAICT 我们自己的所有代码都会正确执行到完成。所有的析构函数都像所有终结部分和类析构函数一样被调用,它们都没有引发任何异常。
此外,madExcept 和 FastMM4 的完全调试模式似乎都没有什么可抱怨的(尽管这可能是一个错误的结论,因为崩溃可能在这些组件自己的最终代码运行之前发生)。
那么,你会怎么做?你会从哪里开始?
这个问题应该更多地是关于这类问题的一般方法,而不是关于我目前面临的具体实例,所以我故意省略了细节。随意询问您是否认为它们可能与调试方法的选择有关,我稍后会添加它们。
【问题讨论】:
存在一个 VCL 错误,在关闭时表现为 AV。它与Owner
破坏表单有关。我通过在Application.Run
之后显式关闭 .dpr 文件中的表单来解决此问题。如果您让Application
为您关闭,那么过时的引用可能会出现问题。
@David:听起来很有趣。你有那个 QC 号码吗?但是,除了主表单之外,我将在代码中创建所有表单,并将相应的父表单作为所有者。您所说的问题是否也适用于这种情况?
如果您依靠破坏应用程序来降低其他所有内容,那么我正在谈论的错误可能适用。尝试在 .dpr 文件中添加 MainForm.Free
。我去看看 QC 号码。
@大卫:谢谢!我去看看。
【参考方案1】:
运行时错误 216 表示您有 Av(访问冲突)并且 SysUtils 已经停止将这些错误转换为异常。
第一次尝试:使用调试 DCU 构建并查看引发错误的单位系统,在那里设置断点。希望您可以在调试器中捕获它并从那里开始工作。
您可能有内存错误(悬空指针、空引用等在已完成的单元中使用 s 字符串常量),最好的技巧是在 sysutils 完成后检查完成情况。您可以通过构建 WITH 调试 dcu、在 sysutils 中将断点设置为最终确定并开始逐步执行代码直到发生错误来做到这一点。
【讨论】:
【参考方案2】:您是否使用运行时包?我以前见过类似的问题。如果您要跨包边界共享全局变量或接口,则必须确保在卸载该包之前清除对属于某个包的类的所有引用;否则他们会尝试对不再有效的内存进行虚拟调用。
【讨论】:
哦,是的,不仅是运行时包,还有依赖于这些包的 COM-DLL(即插件)(主机 EXE 也是如此)。不要问。我今天不会再这样做了,但它已经完美运行了 10 多年了……不过,我会仔细检查对某些全球单例的任何引用。这听起来确实很有希望...... “完美运行 10 多年了” 发生了什么变化?这可能就是我解决这个问题的方式。 触摸! ;) 对于一个公认的答案来说,这几乎已经足够好了。 :-P【参考方案3】:Runtime error 216 是内存问题 (Access violation),您似乎引用了某个当时不存在的对象。
Emarcadero 写道:
使用 SysUtils 类的应用程序将大多数运行时错误映射到异常,这允许您的应用程序在不终止的情况下解决错误
您是否尝试在 Sysutils 的终结部分设置断点?
我会尝试使用分配/内存分析器,它不确定您是否会找到错误代码行,但这可能会向您显示发生内存问题的代码的某些部分。
【讨论】:
【参考方案4】:您可能遇到了指针问题。某些事件或方法试图在不再存在的对象上运行。
【讨论】:
是的,这就是“运行时错误 216”或多或少的含义,但问题是您将如何追踪该问题? 如果您可以可靠地重现它,那么您可以使用调试器(您需要调试 DCU)来确定它发生的位置。不幸的是,我认为你很可能在它引发的时候有一个非常绝望的堆栈跟踪。 @David:这个问题的“可靠再现”仍然是一个问题:我会说它发生在大约 60% 的时间。而且我还无法确定工作会话和非工作会话之间的区别可能是什么。我实际上已经认为我已经解决了几次问题,只是在几次会议后再次出现......【参考方案5】:“运行时错误 216”来自 Windows 本身,而不是 Delphi 异常处理程序。我发现它是由在单元的初始化和终结部分中运行的代码引起的,这些代码在 Delphi 异常处理程序启动之前执行。特别是。在 Delphi 应用程序终止后运行的终结代码需要卸载的 COM 对象将导致此错误和类似错误。所以检查那些东西。
MNG
【讨论】:
不完全:见单位制:函数 MapToRunError 将 NT 错误代码 STATUS_ACCESS_VIOLATION 映射到 ErrCode := 216。 感谢您的评论——无论如何,这发生在默认的 Delphi 应用程序异常处理程序初始化之前或之后。单元在 Delphi 应用程序启动之前基于 uses 子句加载,在应用程序终止后卸载 - 初始化和终结部分在单元加载和卸载时运行,因此在其中运行的代码不会由 Delphi 应用程序异常处理程序处理。这就是为什么你会得到一个神秘的“运行时错误 216”或 217 并且没有异常消息“Eaccess 违规..@”等。 这不是真的; 216 来自 Delphi RTL,在异常处理机制被取消挂钩之后。这是告诉有 AV 的德尔福方式。 216 不是窗体。 请参见上面的解释。【参考方案6】:就像其他人所说:216 表示 SysUtils 关闭后的 AV。通常,在 SysUtils 之后关闭(并有机会提高 AV)的唯一东西是系统单元。具体来说:内存管理器。
因此,关闭时出现运行时错误 216 通常意味着您的应用程序中存在内存损坏错误。
这很容易解决 - 只需在内存管理器中启用完全调试模式或使用调试内存管理器。然而,有时它可能很难找到。但是你可以先从MM的debug模式开始。
见this article。
【讨论】:
以上是关于如何调试仅在应用程序关闭时发生的崩溃? (德尔福)的主要内容,如果未能解决你的问题,请参考以下文章
当系统清除内存并关闭 UIViewController 时,iOS 崩溃
本地化应用程序仅在非基本语言上崩溃,并且仅在未使用Xcode运行时崩溃