如何找出Delphi函数可能抛出的异常?
有没有一种好的方法来找出过程/函数可能在Delphi中引发哪些异常(包括被称为过程/函数)?
在Java中,我们始终必须声明可以抛出哪些异常,但是在Delphi中情况并非如此,这可能导致未处理的异常。
是否有任何代码分析工具可以检测未处理的异常?
解决方案
回答
看看http://www.madshi.net/madExceptDescription.htm
回答
除了对" raise"关键字进行扫描之外,Delphi中没有语言构造可以告诉临时读者可以从方法中预期哪些异常。
在运行时,可以在每个方法中添加一个包罗万象的异常处理程序,但这是不可取的,因为这会减慢执行速度。 (这样做也很麻烦)。
在方法中添加异常处理块将为其添加一些汇编指令(即使未触发异常时),这在经常调用该方法时会形成可测量的减慢速度。
确实存在一些可以分析运行时异常的库,例如madExcept,JclDebug和EurekaLog。这些工具可以记录有关异常的各种详细信息,强烈建议使用其中之一!
回答
对于运行时,请尝试Eurekalog。我不知道是否存在用于设计时的工具。即使我们拥有没有源代码的第三方代码,也将有更多细节。 Delphi中不需要捕获异常,因此我们不必像Java中那样声明它们。
我想说的是,Delphi不需要处理异常。它将终止程序。 EurekaLog提供了记录已处理和未处理的异常的方法,并在异常发生时提供了有关程序状态的大量信息,包括它发生的代码行和当时的调用堆栈。
回答
任何未在特定级别显式或者普遍处理的异常将在调用堆栈中向上滴入。 Delphi RTL(运行时库)将生成一组不同的异常类(数学错误,访问错误,类特定的错误等)。我们可以选择以特殊方式或者一般地以不同的尝试(除块以外)来处理它们。
除非我们需要传播带有该异常的特定功能上下文,否则我们实际上不需要声明任何新的异常类。
正如以前的评论者所写,我们还可以添加诸如MadExcept或者EurekaLog之类的所有异常处理程序之母,以捕获未捕获的异常。
编辑:这是针对未处理异常的全面保险
try ThisFunctionMayFail; except // but it sure won't crash the application on e:exception do begin // something sensible to handle the error // or perhaps log and/or display the the generic e.description message end end;
回答
我的猜测是,我们试图使Delphi表现得像Java,但这不是一个好方法。我建议不要过分担心未处理的异常。在最坏的情况下,它们会冒泡到通用VCL异常处理程序,并导致Windows消息对话框。在正常的应用程序中,他们不会停止应用程序。
编写良好的代码将记录可能引发的各种异常,以便我们以有意义的方式处理它们。不建议使用包罗万象的处理程序,因为如果我们不知道为什么引发异常,实际上就无法知道该怎么办。我也可以强烈推荐madExcept。
回答
(编辑:现在很明显,该问题仅涉及设计时检查。)
新答案:
我无法说明是否有任何工具可以进行检查。帕斯卡分析仪,其中一个没有。
但是,我可以告诉我们,在大多数Delphi应用程序中,即使有工具可以为我们进行检查,也不会得到任何结果。
为什么?因为TApplication.Run()中的主消息循环将所有HandleMessage()调用包装在一个异常处理块中,该块捕获所有异常类型。因此,在大多数应用程序中,我们将拥有大约99.999%的代码隐式/默认异常处理。在大多数应用程序中,这种异常处理将是我们自己代码的100%左右,未包装在异常处理中的0.001%的代码将是自动生成的代码。
如果有可用的工具为我们检查此情况,则需要重写Application.run()使其不包含异常处理。
(以前的答案:
可以分配Application.OnException事件处理程序以捕获其他异常处理程序未处理的所有异常。尽管这是运行时,因此可能不完全是我们所追求的(听起来像我们想在设计时识别它们),但它确实允许我们捕获任何未在其他地方处理的异常。结合Jedi代码库中的JCLDebug东西之类的工具,我们可以记录堆栈跟踪信息以查找发生异常的位置和原因,这将允许进一步调查并针对有罪代码添加特定的异常处理或者预防措施。 )
回答
我将第二(或者第三)是MadExcept。我已经成功地在几个商业应用程序中成功使用了它,没有任何问题。 MadExcept的好处是,它将为我们生成具有完整堆栈跟踪的报告,通常会向我们指出发生问题的正确方向,甚至可以包括屏幕截图,并会自动将其发送给我们只需单击鼠标即可从客户端计算机上进行操作。
但是,我们不想将其用于所有异常,只是为了捕捉错过的异常。例如,如果我们打开一个数据库并且登录失败,那么最好自己捕获并处理该数据库,而不是在应用程序出现的消息中向用户提供MadExcept默认错误。
回答
简短的答案是,没有工具可以满足要求,甚至扫描一下raise关键字也无法助我们一臂之力。 EAccessViolation或者EOutOfMemory只是可能在几乎任何地方引发的众多异常中的两个。
关于Delphi的一个基本问题是,异常是分层的:所有定义的语言异常都是从Exception派生的,尽管值得注意的是实际上有可能引发任何TObject派生。
如果要捕获特定过程中引发的所有异常,只需将其包装在try / except块中,但是不建议这样做。
// Other code . . . try SomeProcedure() except // BAD IDEA! ShowMessage('I caught them all!'); end;
这将捕获所有内容,甚至包括引发TObject的实例。尽管我认为这很少是最好的做法。通常,我们想使用一个try / finally块,然后允许全局异常处理程序(或者一个最终的try / except块)实际处理异常。
回答
正如Jim McKeeth指出的那样,我们无法获得确切的答案,但在我看来,可以通过一些静态分析来部分回答这个问题:给定特定的函数/过程,构造一个调用图。检查该调用图中的每个函数是否有引发语句。例如,这将告诉我们TIdTcpClient.ReadString可以引发EIdNotConnected(以及其他)。
聪明的分析器可能还会注意到,某些代码使用/运算符并可能包括EDivByZero,或者某些过程访问数组并包含ERangeError。
这个答案比简单地为"加薪"打招呼要严格一些。