【问题标题】:How to find out which code calls an already unloaded module?如何找出哪些代码调用了已卸载的模块?
【发布时间】:2011-08-01 14:07:57
【问题描述】:

我遇到了典型的访问冲突:

access violation at 0x4ebb7456: read of address 0x4ebb7456

这发生在当程序的其余部分已经关闭时创建的线程中。

异常发生时主线程正在运行System.FinalizeUnits

我发现该地址属于加载了gdiplus.dll 的内存区域。

如果我向 dpr 文件添加 LoadLibrary('gdiplus.dll') 调用而不在返回的句柄上调用 FreeLibrary,问题就会消失,这样在运行终结部分时不会卸载 gdiplus.dll

如何找出程序的哪个部分创建了导致访问冲突的线程?

有没有办法识别调用释放内存空间的代码?

FastMM 和 madExcept 帮不上什么忙,madExcept 报错窗口出现了,但是又立即关闭,不写日志文件。

我可以将程序拆开,但它是一个不平凡的应用程序,我宁愿使用某种调试技术来解决这个问题。

【问题讨论】:

  • 您是否在项目中的任何地方使用HtmlHelpViewer 单元?
  • 我已经搜索并得到了 0 个结果,但我看到 HHCTRL.OCX 已加载(可能是由第三方组件,我们没有所有这些组件的完整源代码) .
  • 我记得在 HTML 帮助查看器中遇到过类似的关闭问题。当然,这可能不是你的问题。

标签: delphi debugging delphi-2007 access-violation application-shutdown


【解决方案1】:

追踪此问题的第一步可能是检测代码的哪一部分实际创建了线程。在应用程序关闭期间创建线程对我来说听起来像是坏消息,所以我希望确保不会发生这种情况。

至于怎么做,我会使用调试器断点。首先,我会在 Windows.pas 中对 CreateThread 的实现设置一个断点,并使用调试 DCU 运行。查看该断点是否在关机期间触发。

如果它在关机期间没有中断,那么线程是由非 Delphi 代码创建的。我的下一步是打开 CPU 视图并进入CreateThreadCreateThread 的反汇编将从 JMP 指令开始。进入这个,你将在kernel32.CreateThread。现在在这里设置一个断点,看看在关闭期间触发它时调用堆栈是什么。

【讨论】:

  • kernel32.CreateThread 断点在所有线程上触发,除了我正在寻找的线程。但是我设法发现它属于ace32.dll,它是 Sybase Advantage 数据库服务器的数据访问组件。
  • +1 表示“在应用程序关闭期间创建线程对我来说听起来像是坏消息”
【解决方案2】:

我会找到正在加载库的单元(可能是您正在使用的组件)并将其添加到项目文件的顶部(或顶部附近)。这将确保它在您的应用程序关闭后被卸载,并且应该阻止 AV。

因此,例如,如果您使用GdiPlus,您最终会得到这样的结果:

program MyProgram;

uses
  FastMM4,
  GdiPlus,  // <=== this line inserted
  Windows,
  Forms,
  Controls,
  Classes,

这可能会掩盖以后可能会再次困扰您的问题。值得尝试找出哪个单元正在尝试调用已卸载的 DLL 以及执行此操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-12
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    • 1970-01-01
    • 2015-12-14
    • 2011-08-16
    相关资源
    最近更新 更多