我遇到了典型的访问冲突:
access violation at 0x4ebb7456: read of address 0x4ebb7456
这发生在程序的其余部分已经关闭时创建的线程中。
主要线程在异常时运行System.FinalizeUnits
。
我发现地址属于加载了gdiplus.dll
的内存区域。
如果我在dpr文件中添加LoadLibrary('gdiplus.dll')
调用而不在返回的句柄上调用FreeLibrary
,问题就会消失,这样在运行终结部分时不会卸载gdiplus.dll
如何找出程序的哪个部分创建导致访问冲突的线程?
有没有办法识别调用释放的内存空间的代码?
FastMM和madExcept没什么帮助,madExcept错误报告窗口出现了,但是立即关闭并且不写日志文件。
我可以拆开程序,但这是一个非常简单的应用程序,我宁愿使用某种调试技术解决这个问题。
答案 0 :(得分:3)
跟踪此问题的第一步可能是检测代码的哪个部分实际创建了该线程。在应用程序关闭期间创建一个线程听起来像是制作中的坏消息,因此我希望确保不会发生这种情况。
至于如何做,我会使用调试器断点。首先,我在Windows.pas中设置CreateThread
的实现断点并使用调试DCU运行。查看在关闭期间是否触发该断点。
如果在关闭期间没有中断,那么该线程由非Delphi代码创建。我的下一步是打开CPU视图并进入CreateThread
。 CreateThread
的反汇编将以JMP
指令开头。走进这个,你将在kernel32.CreateThread
。现在在这里设置一个断点,看看在关机期间触发它时调用堆栈是什么。
答案 1 :(得分:2)
我会找到正在加载库的单元(可能是您正在使用的组件)并将其添加到项目文件的顶部(或顶部附近)。这将确保在以后关闭应用程序时卸载它,并且应该阻止AV。
因此,例如,如果您使用的是GdiPlus,那么您最终会得到类似的结果:
program MyProgram;
uses
FastMM4,
GdiPlus, // <=== this line inserted
Windows,
Forms,
Controls,
Classes,
这可能会掩盖一个问题,这个问题可能会在以后再次出现。值得尝试找出哪个单元试图调用卸载的DLL以及执行此操作。