调试访问冲突错误?
在使用 Delphi 编写应用程序时,您可以分享哪些技巧来帮助查找和修复访问冲突?
我相信访问冲突通常是由于尝试访问内存中尚未创建的某些内容(例如对象等)引起的?
我发现很难确定是什么触发了访问违规,然后在哪里进行所需的更改以尝试停止/修复它们。
一个例子是我现在正在进行的个人项目。我在 TTreeView Node.Data 属性中存储每个节点的一些数据。可以多次选择和导出节点(导出会迭代每个选定的节点并将特定数据保存到文本文件中 - 保存到文本文件的信息就是存储在nodes.data 中的信息)。文件也可以导入到Treeview中(将文本文件的内容保存到node.data中)。
该示例中的问题是,如果我将文件导入树视图然后导出它们,它就可以完美工作。但是,如果我在运行时添加一个节点并导出它们,我会得到:
“模块“Project1.exe”中地址 00405772 处发生访问冲突。读取地址 00000388。”
我对此的想法一定是我将数据分配给创建的节点的方式,可能与导入数据时分配数据的方式不同,但对我来说一切看起来都不错。访问冲突仅在导出时出现,导入文件时不会出现这种情况。
我并不是在寻找对上面示例的修复,而是主要提供如何查找和修复此类错误的建议/提示。我不经常遇到访问冲突,但是当我遇到这种情况时,它们真的很难追踪和修复。
因此,建议和技巧将非常有用。
What tips can you share to help locate and fix access violations when writing applications in Delphi?
I believe access violations are usually caused by trying to access something in memory that has not yet been created such as an Object etc?
I find it hard to identify what triggers the access violations and then where to make the required changes to try and stop/fix them.
A example is a personal project I am working on now. I am storing in TTreeView Node.Data property some data for each node. Nodes can be multiple selected and exported (the export iterates through each selected node and saves specific data to a text file - the information saved to the text file is what is stored in the nodes.data). Files can also be imported into the Treeview (saving the contents of the text files into the node.data).
The issue in that example is if I import files into the Treeview and then export them, it works perfect. However if I add a node at runtime and export them I get:
"Access Violation at address 00405772 in module 'Project1.exe'. Read of address 00000388."
My thoughts on that must be the way I am assigning the data to created nodes, maybe differently to the way I assign it when they are imported, but it all looks ok to me. The access violation only shows up when exporting, and this never happens with imported files.
I am NOT looking for a fix to the above example, but mainly advice/tips how to find and fix such type of errors. I don't often get access violations, but when I do they are really hard to track down and fix.
So advice and tips would be very useful.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这意味着您的代码正在访问不允许的内存部分。这通常意味着您有一个指针或对象引用指向错误的内存。可能是因为它还没有初始化或者已经被释放了。
使用调试器,例如 Delphi。它会告诉您 AV 发生在哪一行代码上。从那里通过查看调用堆栈和局部变量等找出您的问题。有时,如果您使用调试 DCU 进行编译,它也会有所帮助。
如果您没有调试器,因为它只发生在客户端,您可能需要使用 MadExcept 或 JclDebug 通过调用堆栈记录异常并将其发送给您。它为您提供的细节较少,但可能会为您指明正确的方向。
有一些工具可能能够通过更积极的检查来更早地发现此类问题。 FastMM内存管理器有这样的选项。
编辑
因此,您的问题会导致模块“Project1.exe”中地址 00405772 处出现 AV。Delphi 调试器会将您带到正确的代码行(或使用“查找错误”)。
它正在尝试读取地址 00000388 处的内存这非常接近 00000000 (nil),因此这可能意味着访问某个数组或动态数组的指针/引用,如果它是一个字节数组,则它可能是第 388 项。相当大的对象或具有大量字段的记录的字段该对象或记录指针/引用将为nil。
It means your code is accessing some part of the memory it isn't allowed to. That usually means you have a pointer or object reference pointing to the wrong memory. Maybe because it is not initialized or is already released.
Use a debugger, like Delphi. It will tell you on what line of code the AV occurred. From there figure out your problem by looking at the callstack and local variables etc. Sometimes it also helps if you compile with Debug DCUs.
If you don't have a debugger because it only happens on a client side, you might want to use MadExcept or JclDebug to log the exception with callstack and have it send to you. It gives you less details but might point you in the right direction.
There are some tools that might be able to find these kind of problems earlier by checking more aggressively. The FastMM memory manager has such options.
EDIT
So your problem results in a AV at addresss 00405772 in module 'Project1.exe'. The Delphi debugger will bring you to the right line of code (or use Find Error).
It is trying to read memory at address 00000388. That is pretty close to 00000000 (nil), so that would probably mean accessing some pointer/reference to an array or dynamic array that is nil. If it was an array of bytes, it would be item 388. Or it could be a field of a rather large object or record with lots of fields. The object or record pointer/reference would be nil.
我发现当我在调试器中运行时,真正难以发现的访问冲突并不总是发生。更糟糕的是,它们发生在顾客身上,而不是我身上。接受的答案提到了这一点,但我真的认为应该给出更多细节:MadExcept 提供了一个堆栈回溯,它为我提供了有价值的上下文信息,并帮助我查看代码在哪里失败,或者存在未处理的异常(这不仅仅是针对访问冲突)。它甚至为客户提供了一种直接从程序内部通过电子邮件向您发送错误报告的方法。这会导致 Beta 测试人员或用户发现并修复更多访问违规行为。
其次,我注意到编译器提示和警告实际上是在为您检测一些常见问题。清理提示和警告,您可能会发现许多访问违规和其他微妙的问题。例如,忘记正确声明析构函数可能会导致编译器警告,但会在运行时导致严重问题。
第三,我发现 Peganza 的 Pascal Analyzer 等工具以及 Delphi 某些版本中的审核和指标功能可以帮助您找到代码中存在问题的区域。作为一个具体的例子,Pascal 分析器发现了我忘记做一些重要事情的地方,这导致了崩溃或访问冲突。
第四,让其他开发人员批评您的代码的技术是无可比拟的。之后你可能会感到有点羞怯,但希望你会学到一些东西,并在做你正在做的事情上做得更好。很可能,使用树视图的方法不止一种,完成您正在做的工作的方法也不止一种,更好的架构和干净的做事方式将产生更可靠的代码,每次触摸时都不会破裂。生成干净代码并不存在有限的规则列表,而是需要一生的努力,而且只是程度的问题。您会惊讶地发现看似无辜的代码可能成为潜在崩溃、访问冲突、竞争条件、冻结和死锁的温床。
I find that the really hard-to-find access violations don't always occur while I'm running in a debugger. Worse yet, they happen to customers and not to me. The accepted answer mentions this, but I really think it should be given more detail: MadExcept provides a stack traceback which gives me valuable context information and helps me see where the code fails, or has unhandled exceptions (it's not just for access violations). It even provides a way for customers to email you the bug reports right from inside your program. That leads to more access violations found and fixed, reported by your beta testers, or your users.
Secondly, I have noticed that compiler hints and warnings are in fact detecting for you, some of the common problems. Clean up hints and warnings and you might find many access violations and other subtle problems. Forgetting to declare your destructors properly, for example, can lead to a compiler warning, but to serious problems at runtime.
Thirdly, I've found tools like Pascal Analyzer from Peganza, and the audits-and-metrics feature in some editions of Delphi, can help you find areas of your code that have problems. As a single concrete example, Pascal Analyzer has found places where I forgot to do something important, that lead to a crash or access violation.
Fourth, you can hardly beat the technique of having another developer critique your code. You might feel a bit sheepish afterwards, but you're going to learn something, hopefully, and get better at doing what you're doing. Chances are, there is more way than one to use a tree view, and more way than one to do the work you're doing, and a better architecture, and a clean way of doing things is going to result in more reliable code that doesn't break each time you touch it. THere is not a finite list of rules to produce clean code, it is rather, a lifetime effort, and a matter of degrees. You'd be surprised how innocent looking code can be a hotbed of potential crashes, access violations, race conditions, freezes and deadlocks.
我想提一下另外一个工具,当其他工具无法检测 AV 时我会使用它。它是 SafeMM (新版本)。有一次它向我指出了 5 行小程序。我不得不看了十多分钟,才能看到那里发生的 AV。也许那天我的编程技能还没有发挥到最大,但是你知道,坏事往往就在这样的日子里发生。
I would like to mention one more tool, that I use when other tools fail to detect AV. It's SafeMM (newer version). Once it pointed me to the small 5 line procedure. And I had to look more than 10 minutes at it, in order to see the AV that happened there. Probably that day my programming skills wasn't at their maximum, but you know, bad thing tend to happen exactly at such days.
只是想提一下之前的答案中未提及的其他调试或“代码保护”技术:
“本地”工具:
* 在调试模式下使用 FastMM - 每次释放内存时都写入零。这将使您的程序极其缓慢,但您有很大的机会发现错误,例如尝试访问已释放的对象。
* 使用 FreeAndNil(Obj) 代替 Obj 。自由的。有些人抱怨它会产生问题,但实际上没有提供可能发生这种情况的明确示例。此外,Emarcadero 最近在其手册中添加了使用 FreeAndNil 的建议(终于!)。
* 始终在发布和调试模式下编译应用程序。确保项目选项已正确设置为调试模式。调试模式的默认设置不正确/不完整 - 最后在 Delphi XE7 和 Tokyo 中不正确/不完整。也许有一天他们会为调试模式设置正确的选项。因此,启用类似:
第3方工具:
Just want to mention other debugging or "code guard" techniques that were not mentioned in previous answers:
"Local" tools:
* Use FastMM in DebugMode - have it write zeros each time it dealocates memory. This will make your program PAINFULLY slow but you have a HUGE chance to find errors like trying to access a freed object.
* Use FreeAndNil(Obj) instead of Obj.Free. Some, people were complaining about it as creating problems but without actually providing a clear example where this might happen. Additionally, Emarcadero recently added the recommendation to use FreeAndNil in their manual (finally!).
* ALWAYS compile the application in Release and Debug mode. Make sure the Project Options are correctly set for debug mode. The DEFAULT settings for Debug mode are NOT correct/complete - at last not in Delphi XE7 and Tokyo. Maybe one day they will set the correct options for Debug mode. So, enable things like:
3rd Party Tools: