我知道如何在错误消息包含实际地址时创建.map文件来跟踪访问冲突错误。
但是,如果错误消息显示
,该怎么办?Access violation at address 00000000. Read of address 00000000.
我从哪里开始寻找导致此问题的原因......?
答案 0 :(得分:33)
接受的答案并不能说明整个故事。
是的,每当看到零时,都会涉及NULL
指针。这是因为NULL
按定义为零。因此,调用零NULL
可能并不多说。
对您收到的消息感兴趣的是NULL
被提及两次。实际上,您报告的消息看起来有点像Windows品牌操作系统向用户显示的消息。
消息显示地址 NULL
试图读取 NULL
。那是什么意思呢?具体来说,地址如何自行阅读?
我们通常会考虑在某个地址读取和写入内存的地址处的指令。知道这允许我们解析错误消息。该消息试图阐明地址 NULL
处的指令试图读取 NULL
。
当然,地址NULL
没有说明,这就是为什么我们认为NULL
在我们的代码中是特殊的。但是,每一条指令都可以被认为是从阅读本身的尝试开始。如果CPU EIP
寄存器位于地址NULL
,则CPU将尝试从地址0x00000000(NULL
)读取操作码。此读取NULL
的尝试将失败,并生成您收到的消息。
在调试器中,当收到此消息时,请注意EIP
等于0x00000000。这证实了我给你的描述。
接下来的问题是,“为什么我的程序会尝试执行NULL
地址。”我想到了三种可能性:
NULL
,从未进行过初始化,并且正在取消引用。NULL
条目。这些是在您的代码中使用语法virtual function_name()=0
。ret
指令时,将从覆盖的内存点加载值0x00000000(NULL
)。这种类型的错误,堆栈溢出,是我们论坛的同名词。由于您提到您正在调用第三方库,我将指出库可能是您希望提供非NULL
函数指针作为某些API的输入。这些有时被称为“回叫”功能。
您必须使用调试器进一步缩小问题的原因,但上述可能性应该可以帮助您解决问题。
答案 1 :(得分:29)
地址'00000000'附近任何地方的访问冲突都表示空指针访问。你在创建它之前使用的东西很可能,或者在它被FreeAndNil()之后使用。
很多时候,这是因为在表单创建过程中访问错误位置的组件,或者让主表单尝试访问尚未创建的数据模块中的某些内容。
MadExcept可以很容易地跟踪这些内容,并且可以免费用于非商业用途。 (实际上,商业使用许可证也很便宜,非常物有所值。)
答案 2 :(得分:7)
您开始寻找您知道运行的代码,并且当您到达知道未运行的代码时,您就会停止查看。
您正在寻找的可能是您的程序通过函数指针调用函数的地方,但该指针为空。
你也可能有堆栈损坏。您可能已将函数的返回地址覆盖为零,并且异常发生在函数的末尾。检查可能的缓冲区溢出,如果要调用任何DLL函数,请确保使用正确的调用约定和参数计数。
这不是使用空指针的常见情况,如未分配的对象引用或PChar。在这些情况下,您将获得非零“地址 x ”值。由于指令发生在地址0,因此您知道CPU的指令指针未指向任何有效指令。这就是调试器无法显示导致问题的代码行的原因 - 没有代码行。您需要通过查找指向CPU跳转到无效地址的位置的代码来找到它。
调用堆栈可能仍然完好无损,这至少应该让您非常接近目标。但是,如果您有堆栈损坏,则可能无法信任调用堆栈。
答案 3 :(得分:3)
如果您收到“地址00000000处的访问冲突”,则表示您正在调用尚未分配的函数指针 - 可能是事件处理程序或回调函数。
例如
type
TTest = class(TForm);
protected
procedure DoCustomEvent;
public
property OnCustomEvent : TNotifyEvent read FOnCustomEvent write FOnCustomEvent;
end;
procedure TTest.DoCustomEvent;
begin
FOnCustomEvent(Self);
end;
而不是
procedure TTest.DoCustomEvent;
begin
if Assigned(FOnCustomEvent) then // need to check event handler is assigned!
FOnCustomEvent(Self);
end;
如果错误发生在第三方组件中,并且您可以追踪违规代码,请使用空事件处理程序来阻止AV。
答案 4 :(得分:2)
当我偶然发现这个问题时,我通常会开始查看FreeAndNil()或xxx:= NIL的地方;变量和之后的代码。
当没有其他任何帮助时,我已经添加了一个Log()函数来在执行期间从各个可疑位置输出消息,然后查看该日志以跟踪代码中访问冲突的位置。
有许多更优雅的解决方案可用于追踪这些违规行为,但如果您没有随意使用旧式试用版和错误方法工作正常。
答案 5 :(得分:1)
这可能是因为您直接或间接通过访问NULL指针的库调用。在这种特殊情况下,看起来你已经跳到了一个空洞的地址,这是一个有点毛茸茸的地方。
根据我的经验,跟踪这些问题的最简单方法是使用调试器运行它,并转储堆栈跟踪。
或者,您可以“手动”执行此操作并添加大量日志记录,直到您可以准确地跟踪此违规发生的功能(以及可能的LOC)。
查看Stack Tracer,这可能会帮助您改进调试。
答案 6 :(得分:1)
使用MadExcept。或者JclDebug。
答案 7 :(得分:1)
我将继续疯狂和类似的工具,如Eurekalog,但我认为你也可以使用FastMM。启用完整的调试模式后,它应该为您提供一些错误的线索。
无论如何,即使Delphi使用FastMM作为默认值,也值得获取完整的FastMM,因为它可以对日志记录进行额外的控制。
答案 8 :(得分:-2)
这是一个真正的快速临时修复,至少在你再次重启之前,它将摆脱持久访问。我安装了一个工作正常的程序但由于某种原因,有一点没有在正确的文件中正确安装。因此,当它无法访问该文件时,它会弹出拒绝访问权限,而不是只有一个,它会一直尝试启动它,所以即使搜索位置永久停止它,它也会继续弹出越来越多的每一个3秒。要阻止这种情况至少暂时发生,请执行以下操作...
这将阻止窗口持续弹出,至少在重新启动之前。我知道这并没有解决问题但是像任何事情一样,有一个消除的过程,这里的这一步至少会让它变得不那么烦人。