Delphi 7异常未被捕获

3

我一直在处理一些非常复杂的旧代码,当收集大量数据时会崩溃。我一直无法找到崩溃的确切原因,并尝试不同的方法来解决它或至少恢复正常。我最后做的一件事是将崩溃的代码放入了一个

try
  ...
except
  cleanup();
end;

只是为了使其正常运行。但是清理工作从未完成。在什么情况下会发生未捕获异常?这可能是由于某些内存溢出或其他原因,因为该应用程序正在收集大量数据。

哦,而在添加try之前我得到的异常是“访问冲突”(还有什么?)并且CPU窗口指向非常低的地址。任何想法或指针都将不胜感激!

7个回答

9
“非常低的地址”可能意味着有人试图在一个不存在的对象(即“nil”)上调用虚拟方法。例如:

TStringList(nil).Clear;

虽然第一部分非常神秘,但我不知道那是怎么发生的。

我认为你应该尝试使用 madExcept 来捕捉异常。它从未让我失望过。(免责声明:我没有使用D7。)


我赞同使用madExcept,我在Delphi 7中成功地使用过它。 - Bruce McGee
关于 madExcept 的使用,它非常容易使用,只需将其安装到 IDE 中并为您的项目启用即可。 - skamradt

7
一个被破坏的堆栈或堆栈溢出都会对Windows中结构化异常处理(SEH)使用的堆栈结构造成无法挽回的伤害,从而导致无法找到实际的异常处理程序。
如果在堆栈上的缓冲区(例如作为局部变量的静态数组,但超出了其末尾)中发生了缓冲区溢出,并覆盖了异常记录,则可以覆盖“下一个”指针,该指针指向堆栈上的下一个异常记录。如果指针被损坏,操作系统将无法找到下一个异常处理程序并最终到达您的全捕获处理程序。
堆栈溢出则不同:它们可能根本无法调用函数,因为每个函数调用都需要至少一个dword的堆栈空间来存放返回地址。

这似乎接近真相,我正在进一步调查。谢谢! - Niklas Winde

2
您有很多好的答案。我曾遇到过像巴里提到的堆栈损坏问题一样疯狂的问题。在链接器页面上,项目的“内存大小”部分会出现一些问题。我可能有点迷信,但似乎更大并不一定更好。您可以考虑使用增强型内存管理器FastMM4——它是免费的,并且非常有用。 http://sourceforge.net/projects/fastmm/ 我在D7中使用了它,并发现了一些对陈旧指针的访问和其他恶意事物。
您也可以创建一种跟踪有效对象或以其他方式检测代码的方法,使代码在工作时进行自检。
当我看到像0x00001000或更低的地址时,我想到对空指针的访问。myStringList:=nil; myStringList.Clear;
当我看到其他地址的访问具有更大的数字时,我想到陈旧的指针。
当事情变得奇怪不稳定且堆栈跟踪证明是荒谬的和/或极其变化时,我知道我有堆栈问题。有时在Controls.pas中,下一次在mmsys.pas中等等。
调用DLL时使用错误的调用约定也会使您的堆栈混乱。这是因为调用/返回DLL时的参数传递/释放。
即使它显示无意义,MadExcept也将有助于找到此问题的源头……您将赢得两次,因为您将知道问题发生的位置或您将知道您有堆栈问题。
是否有任何测试框架可以对其进行测试?我发现这非常有用,因为它使其完全可重复。
我用这种方法修复了一些非常丑陋的问题。

1

以前我在调用一些使用safecall调用约定的COM对象时遇到过这种奇怪的行为。这个对象/方法可能会引发一个EOleException,而客户端代码上通常的try/except无法捕获这个异常。 你应该捕获并正确处理一个EOleException。

try
...
except
on E: EOleException do
...
end;

我不确定这是否是你所面临的问题。但如果是的话,我建议你看一下正确实现错误处理,这是一篇非常清晰的有关Delphi异常处理的文章。

你还可以启用IDE调试选项来停止Delphi异常并监视堆栈跟踪。


1

我会把 except 无法工作的原因留给 Barry 来解释...

但我强烈建议采用一种简单的策略来缩小发生问题的范围。 将大块内容分割成由...包围的小块。

try
  OutputDebugString('entering part abc');
  ... // part abc code here
except
  OutputDebugString('horror in part abc');
  raise;
end;
...   
try
  OutputDebugString('entering in part xyz');
  ... // part xyz code here
except
  OutputDebugString('horror in part xyz');
  raise;
end;

并在侧边使用DebugView运行您的代码...(适用于没有GUI的应用程序,如服务)。
您将看到执行的部分以及异常是否在那里捕获。


0

这可能是一个DLL或COM对象吗?如果是,那么主机应用程序可能会将FPUExcpetion掩码设置为与Delphi不同的值。在Delphi中,默认情况下溢出会产生异常,但是可以设置FPUExcpetionmask以使其不产生异常,并且该值设置为NAN。有关FPUExceptionmask的更多信息,请参见math.pas单元。


0

我的代码出现了初始化和结束块中的异常,即使使用madExcept也无法捕获。如果在try块内引用外部DLL,则可能会发生这种情况。我不确定原因。

实际上(感谢@Gung告诉我我古老答案的无用性),我最近在古老的O'Reilly Delphi Tome中阅读到了这篇文章。您应该将SysUtils作为主窗体的DPR中的第一项(或第二项,在您的非标准内存管理器单元之后),以便它与所有其异常捕获好处一起常驻内存。否则,如果它是从其他单元加载的,则它也将随该单元一起卸载,您将无法使用内置的异常处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接