调试访问冲突错误?

时间:2011-06-02 12:24:30

标签: delphi access-violation

在Delphi中编写应用程序时,您可以分享哪些提示来帮助查找和修复访问冲突?

我认为访问冲突通常是由于尝试访问尚未创建的内存中的内容(例如Object等)引起的?

我发现很难确定是什么触发了访问冲突,然后在哪里进行必要的更改以尝试停止/修复它们。

一个例子是我正在研究的个人项目。我在TTreeView Node.Data属性中存储了每个节点的一些数据。节点可以多次选择和导出(导出遍历每个选定节点并将特定数据保存到文本文件 - 保存到文本文件的信息是存储在nodes.data中的信息)。文件也可以导入Treeview(将文本文件的内容保存到node.data中)。

该示例中的问题是,如果我将文件导入Treeview然后导出它们,它就完美了。但是,如果我在运行时添加一个节点并导出它们,我得到:

  

“模块'Project1.exe'中地址00405772的访问冲突。读取地址00000388。”

我对此的看法必须是我将数据分配给已创建节点的方式,可能与导入时分配数据的方式不同,但这对我来说都是好的。访问冲突仅在导出时显示,导入的文件不会发生这种情况。

我不是在寻找上述示例的修复程序,但主要是建议/提示如何查找和修复此类错误。我经常不会违反访问权限,但是当我这样做时,他们很难找到并修复。

所以建议和提示非常有用。

4 个答案:

答案 0 :(得分:23)

这意味着您的代码正在访问不允许的内存的某些部分。这通常意味着您有一个指向错误内存的指针或对象引用。也许是因为它没有初始化或已经发布。

使用调试器,如Delphi。它会告诉你AV出现的代码行。从那里通过查看callstack和局部变量等来解决您的问题。有时,如果使用Debug DCU进行编译,它也会有所帮助。

如果您没有调试器,因为它只发生在客户端,您可能希望使用MadExcept或JclDebug使用callstack记录异常并将其发送给您。它可以为您提供更少的细节,但可能会指出您正确的方向。

通过更积极地检查,有些工具可能能够更早地找到这些问题。 FastMM内存管理器有这样的选择。

修改

  

“地址00405772的访问冲突   在模块'Project1.exe'中。阅读   地址00000388。“

因此,您的问题导致模块“Project1.exe”中的地址为00405772的AV。 Delphi调试器将带您到正确的代码行(或使用Find Error)。

它正在尝试读取地址00000388处的内存。这非常接近00000000(nil),因此这可能意味着访问一些指向数组的指针/引用或nil的动态数组。如果它是一个字节数组,它将是项目388.或者它可能是一个相当大的对象或具有大量字段的记录的字段。对象或记录指针/引用将为零。

答案 1 :(得分:18)

我发现当我在调试器中运行时,并不总是会发生真正难以发现的访问冲突。更糟糕的是,它们发生在顾客而不是我身上。接受的答案提到了这一点,但我真的认为它应该被赋予更多细节:MadExcept提供了一个堆栈回溯,它为我提供了有价值的上下文信息,并帮助我查看代码失败的地方,或者有未处理的异常(它不仅仅是访问违规)。它甚至为客户提供了一种方法,可以直接通过电子邮件向您发送错误报告。这会导致您的Beta版测试人员或您的用户报告发现并修复了更多访问冲突。

其次,我注意到编译器提示和警告实际上是在检测一些常见问题。清理提示和警告,您可能会发现许多访问冲突和其他微妙问题。例如,忘记正确声明析构函数可能会导致编译器警告,但会导致运行时出现严重问题。

第三,我找到了Peganza的Pascal Analyzer等工具,以及Delphi某些版本中的审计和度量功能,可以帮助您找到有问题的代码区域。作为一个具体的例子,Pascal Analyzer找到了我忘记做重要事情的地方,这会导致崩溃或访问冲突。

第四,你很难打败让另一个开发者批评你的代码的技巧。之后你可能会觉得有点羞怯,但是你会学到一些东西,希望能够更好地做你正在做的事情。有可能,使用树视图的方式多于一个,而且比你更多的方式来做你正在做的工作,更好的架构和干净的做事方式将导致更可靠的代码每次触摸它都不会破坏。这不是生成清洁代码的有限规则列表,而是一生的努力和程度问题。你会惊讶地看到无辜的代码可能成为潜在崩溃,访问违规,竞争条件,冻结和死锁的温床。

答案 2 :(得分:3)

我想提一个工具,当其他工具无法检测AV时我会使用它。它是SafeMMnewer version)。一旦它指向我的小5行程序。为了看到那里发生的AV,我不得不看了10多分钟。可能那天我的编程技巧并不是最大的,但是你知道,坏事往往发生在这样的日子。

答案 3 :(得分:1)

只想提及以前的答案中未提及的其他调试或“代码保护”技术:

“本地”工具
  *在DebugMode中使用FastMM-每次释放内存时都将其写入零。这将使您的程序PAINFULLY缓慢,但是您有巨大的机会发现错误,例如尝试访问释放的对象。
  *使用FreeAndNil(Obj)代替Obj.Free。有些人抱怨它造成了问题,但没有提供明确的例子说明可能发生的情况。此外,Emarcadero最近在他们的手册中添加了推荐使用FreeAndNil的建议(最终!)。
  *始终以发布和调试模式编译应用程序。确保为调试模式正确设置了项目选项。调试模式的默认设置不正确/不完整-最终不在Delphi XE7和Tokyo中。也许有一天他们会为“调试”模式设置正确的选项。因此,启用like

  • Stack frames
  • “地图文件生成(详细)”
  • “范围检查”,
  • “符号参考信息”
  • “调试信息”
  • “溢出检查”
  • “断言”
  • “调试DCU”
  • 停用“编译器优化”!

第三方工具:

  • 使用MadShi或EurekaLog(我推荐使用MadShi而不是EurekaLog)
  • 使用Microsoft的ApplicationVerfier