Delphi 2007 - Error handling, not hitting the 'except' section - delphi

I wrote a primitively simple code where I tried to debug the situation that arose in my project. I'm trying to get into an exception when creating a new window, but I don't get there, why is this? Tell me what's wrong? or what to read for understanding.
So I get to the 'except' section!
procedure TForm1.Button1Click(Sender: TObject);
var
Local: TForm2;
begin
try
//Local := TForm2.Create(nil);
//Local.Show;
raise Exception.Create('not implement');
except
on E: Exception do
begin
end;
end;
end;
I need to handle this situation. I don't get into the 'except' section like that
Module Form1.pas
procedure TForm1.Button1Click(Sender: TObject);
var
Local: TForm2;
begin
try
Local := TForm2.Create(nil);
Local.Show;
except
on E: Exception do
begin
end;
end;
end;
Module Form2.pas
procedure TForm2.FormCreate(Sender: TObject);
begin
raise Exception.Create('not implement');
end;

You will see that DoCreate that is called from TCustomForm.Create has OnCreate inside a try and its exception passes the exception to TApplication.HandleException which will pass it to TApplication.OnException if one is assigned.
If think if you override your form's HandleCreateException you can return a result of False which will allow the exception to passthrough

Related

Delphi code completion fail with anonymous methods

Please create a new FMX application, add a button and a memo to run this example. I have this code:
procedure TForm1.Button1Click(Sender: TObject);
begin
TTask.Run(procedure
var
client: TIdHTTP;
result: string;
begin
client := TIdHTTP.Create(nil);
try
try
client.ReadTimeout := 4000;
client.ConnectTimeout := 4000;
result := client.Get('a valid url here just as test');
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(result);
end);
except
on E: Exception do
begin
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(E.Message);
end);
end
end;
finally
client.Free;
end;
end);
end;
It works as I expect but the problem is in the IDE. If I place the cursor somewhere in the body of the anonymous function, I get the closing of the finally statement automatically.
How can I fix this?
First I am here
Then I press enter and I have this!
If you put the cursor at the beginning and not at the end of the line, you can add new spaces without the completion. How to solve this problem? Well, I have discovered that the issue happens because there is this code:
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(result);
end);
If you remove this code, the issue doens't happen anymore. Is this a bug in the IDE?
Is this a bug in the IDE?
Yes. This is a defect. Please submit a report to Quality Portal.
Is this a bug in the IDE?
Yes, this is a bug in the IDE. Your code is syntactically valid.
How can I fix this?
The best way to avoid this is to create your code and surround it with try...except... to handle any exception:
try
MyClass := TComponent.Create(Self);
try
finally
MyClass.Free;
end;
except on E: Exception do
end;
So your code will be:
TTask.Run(procedure
var
client: TIdHTTP;
result: string;
begin
try
Client := TIdHTTP.Create(nil);
try
client.ReadTimeout := 4000;
client.ConnectTimeout := 4000;
result := client.Get('a valid url here just as test');
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(result);
end);
finally
Client.Free;
end;
except on E: Exception do
begin
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(E.Message);
end);
end;
end;
end;

How to handle EAccessViolation when closing a Form

I'm having EAccessViolation when I close the form of my application and I don't know how to deal with this, I have two units, here is the main unit relevant code:
unit MainUnit;
uses
.., myComponent1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
MyComponent1.doSomeWork(p1, p2, ..., pn);
end;
procedure TForm1.OnMyComponen1tEvent(sender: TObject; p: Integer);
begin
memo1.Lines.Add(message);
end;
end.
This unit uses another unit is a component class, in which i send a signal to memo1 to show the message, of course using the component event, it's something like:
unit myComponent;
type
TMyComponentEvent = procedure(sender: TObject; p: integer) of object;
type
TMyComponent = class(TComponent)
// Properties and events declaration
procedure TPThread.Execute;
begin
try
// Create and run some worker threads
// Wait for them to finish the job
// This is the last thing to do:
if Assigned(FOnMyComponentEvent) then
begin
FOnMyComponentEvent(Self, p);
end;
finally
//free ressources
end;
end;
procedure TMyComponent.DoSomeWork;
begin
TPThread.Create(p1, p2 ...);
end;
end.
When I close the form before the program finishes its job ( The threads are still working), i get that exception but sometimes, there is no exception raised. Well, when the exception is raised it indicates the line: memo1.Lines.Add(message);.
I don't know how to solve it, so how can I prevent the exception from happening?
Sounds like you are not setting the MyEvent event to nil when destroying the Form, eg
procedure TForm1.FormCreate(sender: TObject);
begin
OtherUnit.MyEvent := MyEvent;
end;
procedure TForm1.FormDestroy(sender: TObject);
begin
OtherUnit.MyEvent := nil;
end;

FURTHER CLARIFICATION: How to correctly write Try..Finally..Except statements?

RE: How to correctly write Try..Finally..Except statements?
I'm still confused by the OP's original question. Specifically, the last line of the procedure (outside of the try..finally..end) that reads "Screen.Cursor:=crDefault".
My understanding is that any exceptions raised inside a try..except|finally..end block WILL execute the code after the "end" of the "try".
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
Screen.Cursor := crDefault;
end;
In the above example, I don't see any reason why "Screen.Cursor:=crDefault" would not be executed. Please correct me if I'm wrong.
As a further example, I've compiled this little bit of code to help illustrate. When the code is ran, THREE (3) ShowMessage() dialogs will be presented. The first "Exception Raised" and the second "finally" and the third "at end".
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
showMessage(format('%s', [12]));
except
showMessage('Exception raised');
end;
finally
showMessage('finally');
end;
showMessage('at end');
end;
So, I'm confused on why his "Screen.Cursor:=crDefault" isn't being ran, in it's original form and code. Can someone please elaborate?
The code you posted seems to work fine, because you're able to handle all possibilities. Try changing it somewhat though, so that there's an exception raised your code doesn't handle:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('42');
except
on E: EDivByZero do
ShowMessage('DivByZero');
end;
finally
ShowMessage('Finally');
end;
ShowMessage('Got here');
end;
Run this, and you'll see Finally, then the exception for 42, but no Got here message. This is because the exception took you out of the current block, the stack is unwound, and the code from the end of the finally to the end of the procedure never executes.
Move the final ShowMessage call from where it is to inside the finally and run again.
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
raise Exception.Create('42');
except
on E: EDivByZero do
ShowMessage('DivByZero');
end;
finally
ShowMessage('Finally');
ShowMessage('Got here');
end;
ShowMessage('Will never get here');
end;
You'll now see both calls to ShowMessage in the finally block, one after the other, but not the one after the finally block's end;. Code inside the finally block is guaranteed to execute, while code beyond it may or may not.
To make it even more clear, the presence of the try..except block can be removed:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('42');
finally
ShowMessage('Finally');
ShowMessage('Got here');
end;
ShowMessage('Will never get here');
end;
The entire purpose of the try..finally block is to ensure that code inside the finally section will execute before the procedure ends.
In Delphi, the finally block doesn't really handle the exception that occured in the try block. It only guarantees that the code in the finally block will always be executed, whether an exception occured or not in the try block. If an exception really happened in there, it won't get caught. And when an exception didn't get caught, you know what happened to the code below it.
To catch the exception that might be occured, use the try...except... block instead. You can combine these two construct to do those two action: (1) guarantee the execution some piece of code, and (2) catch the exceptions which might occured. The common usage is like this:
try
try
// do something that might cause an exception.
finally
// do something that must be executed WHATEVER happened.
end;
except
// do something ONLY IF an exception has occured.
end;
So, you should change your code and move the Screen.Cursor := crDefault; inside the finally block. In addition, add the try...except... block to surround the try...finally... block. Like this:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
try
// do something.
finally
Obj.Free;
Screen.Cursor := crDefault;
end;
except
ShowMessage('An error has occured!');
end;
end;
Or, if you are not sure that the code Obj := TSomeObject.Create; is safe enough, you should add the second try...finally... block to surround it, like this:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
try
try
Obj := TSomeObject.Create;
try
// do something.
finally
Obj.Free;
end;
finally
Screen.Cursor := crDefault;
end;
except
ShowMessage('An error has occured!');
end;
end;
There, hope it helps :)
You don't actually catch the exception. In this case, upon exception, the "finally" block of code will execute, and then the exception will unwind the stack.

How to pass call stack information to an exception using EurekaLog?

I have a threaded application and for some purpose I want to pass call stack information of a catched exception to a new custom exception:
try
//here an unknown exception is rissen
except
on E: Exception do
begin
if ... then
raise EMyException.Create(E, CallStackOfExceptionEAsString);
end;
end;
What is the best way to do this, preferably using EurekaLog? I am using Delphi 2006 btw.
EurekaLog exposes several event handlers like OnExceptionNotify.
You can implement these in your code. For example: procedure EurekaLogExceptionNotify(
EurekaExceptionRecord: TEurekaExceptionRecord; var Handled: Boolean);
Here you can see a TEurekaExceptionRecord which is defined in ExceptionLog.pas. But you maybe just own the non-source version which works just fine.
The record has a EurekaExceptionRecord.CallStack list. This proprietary list can be converted to TStringsusing the CallStackToStrings method which is also defined in the ExceptionLog unit.
Here is an example where I write the CallStack into a StringList.
CallStackList := TStringList.Create;
try
CallStackToStrings(EurekaExceptionRecord.CallStack, CallStackList);
LogMessage := 'An unhandled exception occured. Here is the CallStack.' + #13#10
+ CallStackList.Text;
finally
CallStackList.Free;
end;
At least from this starting point you should be able to investigate the exposed functions, records etc.. All information is accessible.
EurekaLog provides a function GetLastExceptionCallStack() (defined in unit ExceptionLog.pas).
Using this I have written the following function (based on example code here):
function GetLastEurekalogCallStackAsString(): string;
{$IFDEF EUREKALOG}
var
Stack: TEurekaStackList;
Str: TStringList;
{$ENDIF}
begin
{$IFDEF EUREKALOG}
Stack := GetLastExceptionCallStack();
try
Str := TStringList.Create;
try
CallStackToStrings(Stack, Str);
Result := Str.Text;
finally
FreeAndNil(Str);
end;
finally
FreeAndNil(Stack);
end;
{$ELSE}
Result := '';
{$ENDIF}
end;
So you can write:
try
//here an unknown exception is rissen
except
on E: Exception do
begin
if ... then
raise EMyException.Create(E, GetLastEurekalogCallStackAsString());
end;
end;
EurekaLog 7 has Chained Exception support, which is specifically designed for this task. Just enable it in options (it is enabled by default) and use:
try
// here an unknown exception is rissen
except
on E: Exception do
begin
if ... then
Exception.RaiseOuterException(EMyException.Create(E.Message));
// for old IDEs:
// raise EMyException.Create(E.Message);
end;
end;

How should I re-raise a Delphi exception after logging it?

Do you know a way to trap, log, and re-raise exception in Delphi code?
A simple example:
procedure TForm3.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Bum');
except
on E: Exception do
begin
MyHandleException(E);
end;
end;
end;
procedure TForm3.MyHandleException(AException: Exception);
begin
ShowMessage(AException.Message);
LogThis(AException.Message);
// raise AException; - this will access violate
end;
So I need to re-raise it in the except block but I was wondering if there is a better way to write my own method to handle and (on specific conditions) to re-raise exceptions.
If you want to re-raise the exception only under certain conditions, write
procedure TForm3.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Bum');
except
on E: Exception do
begin
if MyHandleException(E) then
raise;
end;
end;
end;
function TForm3.MyHandleException(AException: Exception): boolean;
begin
ShowMessage(AException.Message);
result := true/false;
end;
Following on from Craig Young's post, I've used something along the lines of the following code successfully. You can preserve the original exception location by using the "at" identifier with the ExceptAddr function. The original exception class type and information is also preserved.
procedure MyHandleException(AMethod: string);
var
e: Exception;
begin
e := Exception(AcquireExceptionObject);
e.Message := e.Message + ' raised in ' + AMethod;
raise e at ExceptAddr;
end;
try
...
except
MyHandleException('MyMethod');
end;
The following will work, but is of course not ideal for 2 reasons:
The exception is raised from a different place in the call stack.
You don't get an exact copy of the exception - especially those classes that add attributes. I.e. you'll have to explicitly copy the attributes you need.
Copying custom attributes can get messy due to required type checking.
.
procedure TForm3.MyHandleException(AException: Exception);
begin
ShowMessage(AException.Message);
LogThis(AException.Message);
raise ExceptClass(AException.ClassType).Create(AException.Message);
end;
The benefits are that you preserve the original exception class, and message (and any other attributes you wish to copy).
Ideally you'd want to call System._RaiseAgain, but alas that is a 'compiler-magic' routine and can only be called by raise;.
You could try to use (system.pas):
function AcquireExceptionObject: Pointer;
AcquireExceptionObject returns a pointer to the current exception object and prevents the exception object from being deallocated when the current exception handler exits.
Note: AcquireExceptionObject increments the exception object's reference count. Make sure that the reference count is decremented when the exception object is no longer needed. This happens automatically if you use the exception object to re-raise the exception. In all other cases, every call to AcquireExceptionObject must have a matching call to ReleaseExceptionObject. AcquireExceptionObject/ReleaseExceptionObject sequences can be nested.
You should be able to just use the Raise command by itself to re-raise the exception:
begin
MyHandleException(E);
Raise;
end;
Old topic but, what about this solution?
procedure MyHandleException(AException: Exception);
begin
ShowMessage(AException.Message);
AcquireExceptionObject;
raise AException;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Bum');
except
on E: Exception do
MyHandleException(E);
end;
end;
It's based on the first code posted by Eduardo.
This way was working for me!
procedure RaiseExceptionFmt(const AFormat: string;
const AArgs: array of const);
begin
raise Exception.CreateFmt(AFormat, AArgs) at ExceptAddr;
end;
I rewrote my method, and now the raise will be the same before.
procedure RaiseInternalExceptionFmt(const AFormat: string;
const AArgs: array of const);
var
LExceptionPtr: Pointer;
begin
LExceptionPtr := AcquireExceptionObject();
try
Exception(LExceptionPtr).Message := Format(AFormat, AArgs);
raise Exception(LExceptionPtr) at ExceptAddr;
finally
ReleaseExceptionObject();
end;
end;
You could acquire the exception object before calling your handler and keep the handler itself one liner. However, you still have a lot of "Try/Except/Do/End" burden.
Procedure MyExceptionHandler(AException: Exception);
Begin
Log(AException); // assuming it accepts an exception
ShowMessage(AException.Message);
raise AException; // the ref count will be leveled if you always raise it
End;
Procedure TForm3.Button1Click(Sender: TObject);
Begin
Try
Foo;
Except On E:Exception Do
MyExceptionHandler(Exception(AcquireExceptionObject));
End;
End;
However, if what you only want to do is to get rid of repetitive error handling code in event handlers, you could try this:
Procedure TForm3.ShowException(AProc : TProc);
Begin
Try
AProc;
Except On E:Exception Do Begin
Log(E);
ShowMessage(E.Message);
End; End;
End;
Reducing your event handler code to this:
Procedure TForm3.Button1Click(Sender: TObject);
Begin
ShowException(Procedure Begin // anon method
Foo; // if this call raises an exception, it will be handled by ShowException's handler
End);
End;
You can also make it work for functions, using parametrized functions:
Function TForm3.ShowException<T>(AFunc : TFunc<T>) : T;
Begin
Try
Result := AFunc;
Except On E:Exception Do Begin
Log(E);
ShowMessage(E.Message);
End; End;
End;
And making ShowException return a value (acting as a passthru):
Procedure TForm3.Button1Click(Sender: TObject);
Var
V : Integer;
Begin
V := ShowException<Integer>(Function : Integer Begin // anon method
Result := Foo; // if this call raises an exception, it will be handled by ShowException's handler
End);
End;
Or even making the anon procedure touch directly the outer scope variable(s):
Procedure TForm3.Button1Click(Sender: TObject);
Var
V : Integer;
Begin
ShowException(Procedure Begin // anon method
V := Foo; // if this call raises an exception, it will be handled by ShowException's handler
End);
End;
There are some limitations on the interaction of variables from inside the body of the anonymous function and the ones defined in the outer scope, but for simple cases like these, you will be more than fine.

Resources