I encountered the following issue in Delphi with a try/except
block.
I have a simple application - one MainForm named fr_MAIN
and one TDataModule
named DM
. DM
is not auto-created, but it is created at run-time in fr_MAIN
's Button2.OnClick
event:
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
has some code in its OnCreate
event:
procedure TDM.DataModuleCreate(Sender: TObject);
begin
raise Exception.Create('this is error!');
// DM code here ...
end;
The problem is that when I click on Button2
, I receive the 'this is error!'
exception message, the rest of the DM code here
does not run - which is correct! But then I also receive the 'DM started!'
message instead of the 'DM not started!'
message.
The exception raised by DM
interrupts the action, but is not caught in the except
block of the form!
Why is this?
TDataModule
1 has special handling of exceptions raised in its OnCreate
event.
The exception is handled here:
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;
By default, TApplication
assigns TApplication.HandleException()
to 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;
So, TDataModule.DoCreate()
is catching the exception and passing it to TApplication.HandleException()
, which then displays a popup dialog by default. And since TDataModule.HandleCreateException()
then returns True, the caught exception is not re-raised. The exception is now considered handled, allowing the program to continue normally to its Showmessage('DM started!');
call.
To avoid the popup dialog when the exception is raised, you can assign a TApplication.OnException
event handler:
Vcl.Forms.TApplication.OnException
Use OnException to change the default behavior that occurs when an exception is not handled by application code. The OnException event handler is called automatically in the HandleException method.
But the exception is still going to be caught and dismissed by TDataModule.DoCreate()
. If you want to avoid that, so the exception propagates up the call stack, don't raise the exception from the TDataModule.OnCreate
event at all. Override the virtual TDataModule.Create()
constructor and raise the exception from there instead.
1: The same thing also happens in TCustomForm
.
The better solution is to fix it for all forms everywhere.
Copy Forms.pas
from the \Vcl\Source folder into either your project folder (or into a common shared library folder so that all projects will benefit from it).
Then change TCustomForm.HandleCreateExcpetion to:
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;
If you're on earlier versions of Delphi, there is no HandleCreateException. You have to fix the caller directly:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With