如何追踪访问冲突“地址00000000”

时间:2010-02-10 13:21:50

标签: delphi

我知道如何在错误消息包含实际地址时创建.map文件来跟踪访问冲突错误。

但是,如果错误消息显示

,该怎么办?
Access violation at address 00000000. Read of address 00000000.

我从哪里开始寻找导致此问题的原因......?

9 个答案:

答案 0 :(得分:33)

接受的答案并不能说明整个故事。

是的,每当看到零时,都会涉及NULL指针。这是因为NULL 按定义为零。因此,调用零NULL可能并不多说。

对您收到的消息感兴趣的是NULL被提及两次。实际上,您报告的消息看起来有点像Windows品牌操作系统向用户显示的消息。

消息显示地址 NULL试图读取 NULL。那是什么意思呢?具体来说,地址如何自行阅读?

我们通常会考虑在某个地址读取和写入内存的地址处的指令。知道这允许我们解析错误消息。该消息试图阐明地址 NULL处的指令试图读取 NULL

当然,地址NULL没有说明,这就是为什么我们认为NULL在我们的代码中是特殊的。但是,每一条指令都可以被认为是从阅读本身的尝试开始。如果CPU EIP寄存器位于地址NULL,则CPU将尝试从地址0x00000000(NULL)读取操作码。此读取NULL的尝试将失败,并生成您收到的消息。

在调试器中,当收到此消息时,请注意EIP等于0x00000000。这证实了我给你的描述。

接下来的问题是,“为什么我的程序会尝试执行NULL地址。”我想到了三种可能性:

  • 您尝试通过已声明的函数指针进行函数调用,已分配给NULL,从未进行过初始化,并且正在取消引用。
  • 同样,您可能正在调用一个“抽象”C ++方法,该方法在对象的vtable中具有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秒。要阻止这种情况至少暂时发生,请执行以下操作...

  1. CTL + 替代 + 德尔
  2. 打开任务管理器
  3. 记下请求访问权限的程序名称(您可以在应用程序的标签页中看到它)
  4. 单击“进程”选项卡
  5. 滚动浏览直到找到与程序名称匹配的进程并单击
  6. 点击结束流程
  7. 这将阻止窗口持续弹出,至少在重新启动之前。我知道这并没有解决问题但是像任何事情一样,有一个消除的过程,这里的这一步至少会让它变得不那么烦人。