我很恳请您就我在Delphi中使用try/except
块遇到的以下问题提出建议。
我有一个简单的应用程序 - 一个名为fr_MAIN
的MainForm和一个名为TDataModule
的{{1}}。 DM
不会自动创建,但会在DM
的{{1}}事件中的运行时创建:
fr_MAIN
Button2.OnClick
在其procedure Tfr_MAIN.Button2Click(Sender: TObject);
begin
try
DM := TDM.Create(nil);
Showmessage('DM started!');
except
on E:Exception do
begin
Showmessage('DM not started!');
end;
end;
事件中包含一些代码:
DM
问题在于,当我点击OnCreate
时,我收到procedure TDM.DataModuleCreate(Sender: TObject);
begin
raise Exception.Create('this is error!');
// DM code here ...
end;
异常消息,Button2
的其余部分未运行 - 这是正确的!但是,我还收到'this is error!'
消息而不是DM code here
消息。
'DM started!'
引发的异常会中断操作,但不会在表单的'DM not started!'
块中捕获!
为什么会这样?
答案 0 :(得分:22)
TDataModule
1 对其OnCreate
事件中引发的异常进行了特殊处理。
此处处理异常:
procedure TDataModule.DoCreate;
begin
if Assigned(FOnCreate) then
try
FOnCreate(Self);
except
if not HandleCreateException then // <-- here
raise;
end;
end;
function TDataModule.HandleCreateException: Boolean;
begin
if Assigned(ApplicationHandleException) then
begin
ApplicationHandleException(Self); // <-- here
Result := True;
end
else
Result := False;
end;
默认情况下,TApplication
会将TApplication.HandleException()
分配给ApplicationHandleException
:
constructor TApplication.Create(AOwner: TComponent);
var
...
begin
inherited Create(AOwner);
...
if not Assigned(System.Classes.ApplicationHandleException) then
System.Classes.ApplicationHandleException := HandleException; // <-- here
if not Assigned(System.Classes.ApplicationShowException) then
System.Classes.ApplicationShowException := ShowException;
...
end;
因此,TDataModule.DoCreate()
正在捕获异常并将其传递给TApplication.HandleException()
,然后默认情况下会显示一个弹出对话框。然后由于TDataModule.HandleCreateException()
返回True,因此不会重新引发捕获的异常。此异常现在被视为已处理,允许程序正常继续进行Showmessage('DM started!');
调用。
要在引发异常时避免弹出对话框,您可以分配TApplication.OnException
事件处理程序:
Vcl.Forms.TApplication.OnException
使用OnException更改时出现的默认行为 异常不由应用程序代码处理。 OnException事件 在HandleException方法中自动调用handler。
但TDataModule.DoCreate()
仍然会抓住并驳回异常。如果你想避免这种情况,那么异常会在调用堆栈中向上传播,根本不会从TDataModule.OnCreate
事件中引发异常。覆盖虚拟TDataModule.Create()
构造函数并从那里引发异常。
1 :同样的事情也发生在TCustomForm
。
答案 1 :(得分:0)
更好的解决方案是针对所有形式的所有地方对其进行修复。
从{strong> \ Vcl \ Source 文件夹中复制Forms.pas
到您的项目文件夹(或公共共享库文件夹,以便所有项目都可以从中受益)。
然后将 TCustomForm.HandleCreateExcpetion 更改为:
function TCustomForm.HandleCreateException: Boolean;
begin
{
If an exception is raised during a form's OnCreate event, the exception is hidden.
This leaves you with an only partially initialized form.
The correct behavior is to **not** eat the exception.
We do that by returning False. The caller will then throw.
}
// Application.HandleException(Self);
// Result := True;
Result := False;
end;
如果您使用的是早期版本的Delphi,则不会出现 HandleCreateException 。您必须直接修复呼叫者:
procedure TCustomForm.DoCreate;
begin
{
If the Form.OnCreate event throws an exception, the exception is eaten, and the caller never knows about it.
Don't do that.
}
if Assigned(FOnCreate) then
begin
//try
FOnCreate(Self);
//except
// Just let it throw. Christ you guys are dense.
//Application.HandleException(Self);
//end;
end;
if fsVisible in FFormState then
Visible := True;
end;