假设我有三个(或更多)程序,其中一些程序互相调用,如下所示,其中任何一个程序都可能失败。
如果其中任何一个失败,我希望'main'程序立即记录失败并终止程序。
在Delphi中使用哪种正确的语法将异常“传回”到每个先前的过程调用?
如果有人可以帮助我获取主程序的Try / except块以确定哪个位失败,那就更好了!
三个程序的示例伪代码和主程序可能如下所示。
(我认为我理解这个原则,与'加注'有关,但是想要一些实际语法和我应该使用的代码的帮助)
//////////////////////////////////////
Procedure DoProcA
begin
try
begin
{stuff}; //stuff that might fall
end;
except
on E : Exception do
begin
LogError ('error in A');
end //on E
end;//try
//////////////////////////////////////
Procedure DoProcB
begin
try
begin
Do ProcC; //another proc that might fail
{other stuff}
end;
except
on E : Exception do
begin
LogError ('error in B');
end //on E
end;//try
//////////////////////////////////////
Procedure DoProcC
begin
try
begin
{Do stuff} //even more stuf fthat might fail
end;
except
on E : Exception do
begin
LogError ('error in C');
end //on E
end;//try
//////////////////////////////////////
//Main programo
begin
try
DoProcA;
DoProcB;
{other stuff}
except
{here I want to be able to do something like
if failure of A, B or C then
begin
LogError ('Failure somewhere in A, B or C');
application.terminate;
end;}
end; //try
end.
答案 0 :(得分:7)
解决这个问题的最佳方法是删除所有这些异常处理程序。使用像madExcept,EurekaLog,JCL Debug等库来记录任何异常,使其一直回到顶级异常处理程序。
尝试为程序中的每个函数添加异常处理程序是站不住脚的。这根本不是如何使用异常的。作为一个广泛的规则,您应该将异常视为不应被捕获的事物。它们代表了异常行为,因此,通常情况下,它们被引发的函数不知道如何处理它们。
所以,停止尝试处理异常。作为指导原则,不要处理它们。如果他们一直到顶级异常处理程序,那么在那里处理它们。如果你使用上面提到的一个库,你将能够获得丰富的调试信息,以帮助你理解为什么首先引发异常。
答案 1 :(得分:6)
记录后,让每个函数重新提升捕获的异常,例如:
Procedure DoProcA;
begin
try
{stuff}; //stuff that might fall
except
on E : Exception do
begin
LogError ('error in A');
raise; // <-- here
end;
end;
end;
Procedure DoProcB;
begin
try
DoProcC; //another proc that might fail
{other stuff}
except
on E : Exception do
begin
LogError ('error in B');
raise; // <-- here
end;
end;
end;
Procedure DoProcC;
begin
try
{Do stuff} //even more stuff that might fail
except
on E : Exception do
begin
LogError ('error in C');
raise; // <-- here
end;
end;
end;
begin
try
DoProcA;
DoProcB;
{other stuff}
except
on E: Exception do
begin
LogError ('Failure somewhere in A, B or C');
//Application.Terminate; // this is not useful unless Application.Run is called first
end;
end;
end.
如果您希望主过程识别WHICH函数失败,您需要将该信息传递到异常链中,例如:
type
MyException = class(Exception)
public
WhichFunc: String;
constructor CreateWithFunc(const AWhichFunc, AMessage: String);
end;
constructor MyException.CreateWithFunc(const AWhichFunc, AMessage: String);
begin
inherited Create(AMessage);
WhichFunc := AWhichFunc;
end;
Procedure DoProcA;
begin
try
{stuff}; //stuff that might fall
except
on E : Exception do
begin
raise MyException.CreateWithFunc('DoProcA', E.Message); // <-- here
end;
end;
end;
Procedure DoProcB;
begin
try
DoProcC; //another proc that might fail
{other stuff}
except
on E : MyException do
begin
raise; // <-- here
end;
on E : Exception do
begin
raise MyException.CreateWithFunc('DoProcB', E.Message); // <-- here
end;
end;
end;
Procedure DoProcC;
begin
try
{Do stuff} //even more stuff that might fail
except
on E : Exception do
begin
raise MyException.CreateWithFunc('DoProcC', E.Message); // <-- here
end;
end;
end;
begin
try
DoProcA;
DoProcB;
{other stuff}
except
on E: MyException do
begin
LogError ('Failure in ' + E.WhichFunc + ': ' + E.Message);
end;
on E: Exception do
begin
LogError ('Failure somewhere else: ' + E.Message);
end;
end;
end.
或者:
type
MyException = class(Exception)
public
WhichFunc: String;
constructor CreateWithFunc(const AWhichFunc, AMessage: String);
end;
constructor MyException.CreateWithFunc(const AWhichFunc, AMessage: String);
begin
inherited Create(AMessage);
WhichFunc := AWhichFunc;
end;
Procedure DoProcA;
begin
try
{stuff}; //stuff that might fall
except
on E : Exception do
begin
raise MyException.CreateWithFunc('DoProcA', E.Message); // <-- here
end;
end;
end;
Procedure DoProcB;
begin
try
DoProcC; //another proc that might fail
{other stuff}
except
on E : Exception do
begin
Exception.RaiseOuterException(MyException.CreateWithFunc('DoProcB', E.Message)); // <-- here
end;
end;
end;
Procedure DoProcC;
begin
try
{Do stuff} //even more stuff that might fail
except
on E : Exception do
begin
raise MyException.CreateWithFunc('DoProcC', E.Message); // <-- here
end;
end;
end;
var
Ex: Exception;
begin
try
DoProcA;
DoProcB;
{other stuff}
except
on E: Exception do
begin
Ex := E;
repeat
if Ex is MyException then
LogError ('Failure in ' + MyException(Ex).WhichFunc + ': ' + Ex.Message)
else
LogError ('Failure somewhere else: ' + Ex.Message);
Ex := Ex.InnerException;
until Ex = nil;
end;
end;
end.
答案 2 :(得分:3)
来自其他语言的开发人员花费大量时间和精力担心“未处理的异常”,但在典型的Delphi表单应用程序中,如果查看Application.Run背后的代码,您将看到如果您让所有异常都将被处理他们一直冒泡到顶端。 (除非你有充分的理由干涉,否则这是首选行为)
通常,最好在异常中添加更多信息,然后重新提升它,即。放手吧。出了点问题,你的调用函数需要知道,这首先是异常的目的。
如果您想记录每个错误,那么一个好的地方就是在Application.OnException事件中。 NB。您的示例是DOS样式的命令行应用程序,而不是典型的Delphi Windows窗体应用程序,不确定这是否是您的意图。如果这只是为了让示例保持简单,那么您实际上已经为自己创建了更多的工作,因为您无权访问Application对象以及随之而来的所有功能。
例如
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := AppException;
end;
procedure TForm1.AppException(Sender: TObject; E: Exception);
begin
if RunningInAutomationMode then
begin
LogError(E.Message);
Application.Terminate;
end
else
Application.ShowException(E);
end;
直接回答你的问题:
Procedure DoProcA;
begin
try
{stuff}; //stuff that might fall
except
on E : Exception do
begin
//LogError ('error in A'); will get logged later, don't want to log twice
E.Message := 'The following error occurred while trying to do whatzit to a whozit: '+E.Message;
raise;
end;
end;
end;
Procedure DoProcB;
begin
try
DoProcC; //another proc that might fail
{other stuff}
except
on E : Exception do
begin
//LogError ('error in B');
E.Message := E.Message + ' (Proc B)';
raise;
end;
end;
end;
Procedure DoProcC;
begin
try
{Do stuff} //even more stuff that might fail
except
on E : Exception do
begin
//LogError ('error in C');
E.Message := 'The following error occurred during procedure C: '+E.Message;
raise; //Note: do not use raise Exception.Create(E.Message); as you will then lose the exception's type, which can be very useful information to have
end;
end;
end;
begin
try
DoProcA;
DoProcB;
{other stuff}
except
on E: Exception do
begin
LogError (E.Message); //this will end up logging all the exceptions, no mater which procedure they occurred in
//Exception has not been re-raised, so code will continue from this point
Exit;
end;
end;
{Some more code} //Called exit above, so that this code won't get called, although it is unlikely you'll have more code outside the try..except block
end.