3

我刚刚将我们的几个自制 Outlook COM 插件从 Delphi 2007 移植到 Delphi 2009,现在遇到了一些非常奇怪的错误(在你问之前:这些错误似乎都与字符串处理没有任何明显的关系),例如当一个人试图第二次调用它们时挂起 Outlook 的模态对话框(第一次似乎一切都很好),但只有当它们从一个特定的事件处理程序调用而不是在其他地方做同样的事情时才会挂起。当我将错误跟踪到特定的代码行并注释掉该行或用不同的代码替换它以达到相同的效果时(例如,通过将否则将通过函数直接调用的代码复制到调用站点),将出现错误走开——通常只是在以后再次出现一些(同样不显眼的)陈述。

在 Delphi 调试器中运行它时,我可以看到冻结通常在GetMem.inc. 至少所有这些问题都是 100% 可重现的……

不用说我们在 Delphi 2007 中编译这些插件时没有这些问题。

现在,我很茫然。我知道我很幸运,但即使我认为自己是一个相当有经验的程序员(虽然主要是在小众领域),但我以前从未真正处理过这类错误。正如这个问题的标题所说,我什至不知道从哪里开始。我可以随心所欲地单步执行代码,但无休止的汇编语句对我来说毫无意义,而且我也不擅长有效地使用 CPU 视图。

此外,我什至不确定这是否是我自己的代码开始的问题(在这种情况下,我实际上倾向于怀疑它)。我们正在大量使用许多第三方库(例如 JCL、ADX、Redemption)。特别是 ADX 仍将其 Delphi 2009 支持标记为“测试版”。

我也尝试使用 FastMM 的 FullDebugMode 并且确实以这种方式在 ADX 中发现了一些错误(例如,在被释放后被修改的块)但是所有这些也发生在我使用 Delphi 2007 编译时,所以它似乎还不是必须的这些最终是观察到的回归的原因。

那么,我该如何处理呢?- 或者更好:我在哪里可以找到一些学习如何处理这个问题的好资源?例如,关于使用 CPU 视图的教程或有效解释和执行 FastMM 报告的教程?这些是正确的工具吗?我还应该去哪里看?

附录:
在这种情况下我应该怀疑哪些类型的代码?什么样的代码甚至有可能在内存中造成如此严重的破坏?我能想到的唯一地方是我的代码执行任何远程接近显式内存操作的地方是在准备 WinAPI 调用时保留一些缓冲区空间。还要记住,我的所有代码在 Delphi 2007 和 Delphi 2009 版本之间都是相同的,而 Delphi 2007 版本没有出现此类问题。

更新:
很有可能促使我发布这个问题的问题现在已经解决了。请参阅下面我自己的答案。

4

4 回答 4

7

获得解决方案的最佳工具可能是内存断点。

调试内存损坏是很痛苦的,所以首先尝试让你的生活尽可能简单:找到一个精确的、保证可重复的、每次都有效的步骤。如有必要,模拟 Outlook 主机,这样您就不需要依赖 Outlook 时间问题或解决空间布局问题等。

您必须获得一组可靠的可重现步骤,这些步骤会在可预测的地址处导致 AV 或其他错误。

然后你要做的是重新启动进程,为引用该地址的任何内容创建一个内存断点集,并熟悉该内存块的生命周期。最小化和合理化您的复制步骤在这里会有所帮助。添加其他断点可能会有所帮助,并且仅在应用程序后期启用内存断点;或使用 D2009 断点的日志记录功能来记录内存值/调用堆栈等,而不是实际闯入调试对象。

于 2009-01-06T14:40:50.037 回答
7

不完全是对更普遍的问题的答案,但很可能是对提示它的特定问题的解决方案:

我有 95% 的把握现在已经确定了问题所在!:)

这是我所做的:

  • 我在编译器中启用了 RangeChecking 和 OverflowChecking
  • 我追踪并修复了所有导致ERangeErrorEIntOverflow异常
    的问题(每个都有一个)
  • 我在启用 FastMM 和 FullDebugMode 的情况下再次运行程序
  • 我终于能够在所有情况下确定问题的原因是对 JCL 函数的调用GetWindowCaption

似乎GetWindowCaption显然还没有检查 Unicode 兼容性:它使用从 API 函数GetWindowTextLength返回的值(返回字符数)作为输入ReallocMem(期望字节数)为GetWindowText(在 Delphi 2009 中返回 WideChars 的缓冲区)。繁荣!该函数为缓冲区分配的内存太少,但GetWindowText只是简单地覆盖了以下内存,从而破坏了块页脚。

我现在已将其作为项目 #4648归档在 JCL 错误跟踪器中

我从中得出的底线是:始终确保修复所有报告的错误!包括(看似)非关键错误,如范围和溢出错误。如果不出意外,它将使调试更加可预测。

于 2009-01-07T18:36:13.540 回答
2

您在 D2007 中发现了双重释放错误,即使它在此版本中看起来确实工作正常,这意味着您需要修复这些错误,因为您很幸运 D2007 版本不需要像这样积极地回收内存由于内存中的“影子持久性”,D2009 版本和错误不会出现。
我会使用 FastMM fulldebugmode 来查找错误代码并尽可能修复它,然后按照 Barry 的建议解决内存使用问题。
有关如何使用集成调试器的功能,以及如何从非中断断点记录信息,您可能需要查看此 CodeRage 3 会话:Delphi Debugging for Dummies

于 2009-01-06T21:03:31.387 回答
0

我会关注系统内置的完整页面堆支持的方向。

这篇文章中查看如何配置它。如果您的内存使用量不太大,这是最容易找到问题的方法。

当内存消耗较重时会变得很棘手 - 但就像我说的,首先尝试完全 peagheap。

于 2009-01-06T19:41:56.920 回答