有没有一种好方法可以找出程序/函数在 Delphi 中可以引发哪些异常(包括它被称为程序/函数)?
在 Java 中,您总是必须声明可以抛出哪些异常,但在 Delphi 中并非如此,这可能会导致未处理的异常。
是否有任何代码分析工具可以检测未处理的异常?
有没有一种好方法可以找出程序/函数在 Delphi 中可以引发哪些异常(包括它被称为程序/函数)?
在 Java 中,您总是必须声明可以抛出哪些异常,但在 Delphi 中并非如此,这可能会导致未处理的异常。
是否有任何代码分析工具可以检测未处理的异常?
(编辑:现在很明显,这个问题只涉及设计时检查。)
新答案:
我无法说明是否有任何工具可以为您检查。帕斯卡分析仪,其中之一,没有。
然而,我可以告诉你,在大多数 Delphi 应用程序中,即使有一个工具可以帮你检查,你也不会得到任何结果。
为什么?因为 TApplication.Run() 中的主消息循环将所有 HandleMessage() 调用包装在一个异常处理块中,该块捕获所有异常类型。因此,在大多数应用程序中,您将拥有大约 99.999% 的代码的隐式/默认异常处理。在大多数应用程序中,这种异常处理将大约 100% 发生在您自己的代码中 - 未包装在异常处理中的 0.001% 的代码将是自动生成的代码。
如果有工具可以为您检查,您需要重写 Application.run() 使其不包括异常处理。
(以前的答案: Application.OnException 事件处理程序可以被分配来捕获所有其他异常处理程序未处理的异常。虽然这是运行时,因此可能不完全是你所追求的(听起来你想要在设计时识别它们),它确实允许您捕获任何未在其他地方处理的异常。结合绝地代码库中的 JCLDebug 等工具,您可以记录堆栈跟踪以找出异常发生的位置和原因,这将允许进一步调查并围绕有罪代码添加特定的异常处理或预防......)
我的猜测是你试图让 Delphi 表现得像 Java,这不是一个好方法。我建议不要太担心未处理的异常。在最坏的情况下,它们会冒泡到通用 VCL 异常处理程序并导致 Windows 消息对话框。在正常的应用程序中,它们不会停止应用程序。
编写良好的代码将记录可能引发的不同异常,以便您可以以有意义的方式处理它们。不建议使用包罗万象的处理程序,因为如果您不知道引发异常的原因,实际上无法知道该怎么做。我也可以强烈推荐 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块)实际处理异常。
任何未在特定级别显式或通常处理的异常都将在调用堆栈中向上传播。Delphi RTL(运行时库)将生成一组不同的异常类——(数学错误、访问错误、类特定错误等)。您可以选择在不同的 try except 块中专门或一般地处理它们。
除非您需要传播带有异常的特定功能上下文,否则您实际上不需要声明任何新的异常类。
正如之前的评论者所写,您还可以添加所有异常处理程序的母亲,例如 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;
我将第二(或第三)MadExcept。我已经在几个商业应用程序中成功使用它,没有任何问题。MadExcept 的好处在于,它会为您生成一份包含完整堆栈跟踪的报告,该报告通常会为您指出错误的正确方向,甚至可以包含屏幕截图,并自动将其通过电子邮件发送给您只需单击鼠标即可从客户端计算机。
但是,您不想将其用于所有异常,只是为了捕获您错过的异常。例如,如果您打开一个数据库并且登录失败,您最好自己捕获并处理这个数据库,而不是在您的应用程序发生消息中向用户提供 MadExcept 默认错误。
对于运行时尝试Eurekalog。我不知道设计时是否存在工具。即使您有没有源代码的第三方代码,您也会遇到更多困难。Delphi 不需要捕获异常,因此您不必像在 Java 中那样声明它们。
我想说的是,Delphi 不需要处理异常。它只会终止程序。EurekaLog 提供了记录已处理和未处理异常的方法,并提供了有关异常发生时程序状态的大量信息,包括异常发生的代码行和当时的调用堆栈。
正如 Jim McKeeth 指出的那样,您无法得到明确的答案,但在我看来,可以通过一些静态分析来部分回答这个问题:给定一个特定的函数/过程,构建一个调用图。检查该调用图中的每个函数以获取 raise 语句。例如,这会告诉您 TIdTcpClient.ReadString 可以引发 EIdNotConnected (等等)。
聪明的分析者可能还会注意到某些代码使用 / 运算符并可能包含 EDivByZero,或者某些过程访问数组并包含 ERangeError。
这个答案比简单地寻找“加注”要严格一些。
单元的终结部分也可以引发异常。我认为这些会溜走……而且也有些问题。
我认为 Delphi IDE 有一个内置的“堆栈跟踪”或“堆栈树”之类的东西。
这个问题让我想起了 Skybuck 的 TRussianRoulette 游戏……谷歌一下,它的代码和答案可能会有所帮助。