MadExcept for delphi is not printing stacktrace after application crash - delphi

Detailed question :
We are trying to capture the stacktrace (bugreport.txt) using MadExcept in a delphi application where a thread is crashing the application with a fatal error. But MadExcept doesn't print any stacktrace after the application crashes. Any ideas why?
OUR CODE :
procedure TMainForm.WSServerExecute(AContext: TIdContext);
begin
try
HTMLExecute(AContext);
except
on E: Exception do
begin
if not(E is EIdException) then
begin
LogData.AddError('HTMLExecute error: ' + E.Message);
madExcept.HandleException;
end;
raise;
end;
end;
end;
This procedure is called when the client makes a websocket connection back to the server. This is a thread produced by the Indy TCPServer component. The HTMLExecute function is what reads and writes packets between the client and server. I've wrapped that in a try..except block to catch any exceptions. The LogData line is what records the error to the Error Log and the madExcept line is supposed to create the bugreport.txt file. The Raise line passes the exception back to Indy so that it knows a fatal error occurred and will abort the thread.

The reason why madExcept is not handling the exception is because you already caught it with on E:Exception do handling it yourself. Just give madExcept.HandleExcept the exception to handle it:
madExcept.HandleException(etNormal, E);

You could try using RegisterHiddenExceptionHandler(stDontDync). See documentation for more details. In your handler then simply do this:
procedure YourHiddenExceptionHandler(const exceptIntf: IMEException; var handled: boolean);
begin
handled := false;
end;
The above is a trick to force madexcept to work also with handled exceptions, of course it is risky to use it in production...

Related

Exception on FormCreate is not thrown

By default any unhandled exception occurring in TForm.OnCreate will lead to an error message beeing shown instead of an exception beeing thrown.
The reason is this code in VCL.Forms:
function TCustomForm.HandleCreateException: Boolean;
begin
Application.HandleException(Self);
Result := True;
end;
All my forms inherit from TMyForm. I plan to override this function to solve the problem:
function TMyForm.HandleCreateException: Boolean;
begin
Result := False;
end;
But I cannot imagine anybody wanting an exception on form creation to be swallowed, leaving a potentially halfly initialized form, yet this code still exists in VCL. This leaves me with the question:
Are there any reasons to not treat unhandled exceptions on form creation this way? Or are there better options to handle my problem?
Edited to clarify the original problem. I have the following code:
try
//...
Frm := TBooForm.Create(...);
Frm.ShowModal;
//...
except
//exception in TBooForm.FormCreate not landing here.
end;
If I don't touch global exception handling, an unhandled exception in TBooForm.FormCreate() will not land in the exception block. Instead Frm will display in halfly initialized state, leading to hard to track errors.

Exceptionhandling with IOmniParallelTask not working

Unhandled exceptions within IOmniParallelTask execution should (as I understand the docs) be caught by the OTL and be attached to IOmniTaskControl instance, which may be accessed by the termination handler from IOmniTaskConfig.
So after setting up the IOmniParallelTask instance with a termination handler like this:
fTask := Parallel.ParallelTask.NoWait.NumTasks(1);
fTask.OnStop(HandleOnTaskStop);
fTask.TaskConfig(Parallel.TaskConfig.OnTerminated(HandleOnTaskThreadTerminated));
fTask.Execute(TaskToExecute);
any unhandled exceptions within TaskToExecute:
procedure TFormMain.TaskToExecute;
begin
Winapi.Windows.Sleep(2000);
raise Exception.Create('async operation exeption');
end;
should be attached to the IOmniTaskControl instance you get within the termination handler:
procedure TFormMain.HandleOnTaskThreadTerminated(const task: IOmniTaskControl);
begin
if not Assigned(task.FatalException) then
Exit;
memo.Lines.Add('an exception occured: ' + task.FatalException.Message);
end;
The issue at this point is, that the exception is not assigned to IOmniTaskControl.FatalException and I have no clue why.
Maybe some of you guys have some ideas on what I am doing wrong. The whole VCL sampleproject may be found here: https://github.com/stackoverflow-samples/OTLTaskException
This is an abstraction layer problem. Parallel.ParallelTask stores threaded code exception in a local field which is not synchronized with the IOmniTaskControl.FatalException property. (I do agree that this is not a good behaviour but I'm not yet sure what would be the best way to fix that.)
Currently the only way to access caught exception of an IOmniParallelTask object is to call its WaitFor method. IOmniParallelTask should really expose a FatalException/DetachException pair, just like IOmniParallelJoin. (Again, an oversight, which should be fixed in the future.)
The best way to solve the problem with the current OTL is to call WaitFor in the termination handler and catch the exception there.
procedure TFormMain.HandleOnTaskThreadTerminated(const task: IOmniTaskControl);
begin
try
fTask.WaitFor(0);
except
on E: Exception do
memo.Lines.Add('an exception occured: ' + E.Message);
end;
CleanupTask;
end;
I have also removed the HandleOnTaskStop and moved the cleanup to the termination handler. Otherwise, fTask was already nil at the time HandleOnTaskThreadTerminated was called.
EDIT
DetachException, FatalException, and IsExceptional have been added to the IOmniParallelTask so now you can simply do what you wanted in the first place (except that you have to use the fTask, not task).
procedure TFormMain.HandleOnTaskThreadTerminated(const task: IOmniTaskControl);
begin
if not assigned(fTask.FatalException) then
Exit;
memo.Lines.Add('an exception occured: ' + FTask.FatalException.Message);
CleanupTask;
end;
EDIT2
As noted in comments, OnTerminate handler relates to one task. In this example this is not a problem as the code makes sure that only one background task is running (NumTasks(1)).
In a general case, however, the OnStop handler should be used for this purpose.
procedure TFormMain.btnExecuteTaskClick(Sender: TObject);
begin
if Assigned(fTask) then
Exit;
memo.Lines.Add('task has been started..');
fTask := Parallel.ParallelTask.NoWait.NumTasks(1);
fTask.OnStop(HandleOnStop);
fTask.Execute(TaskToExecute);
end;
procedure TFormMain.HandleOnStop;
begin
if not assigned(fTask.FatalException) then
Exit;
memo.Lines.Add('an exception occured: ' + FTask.DetachException.Message);
TThread.Queue(nil, CleanupTask);
end;
As HandleOnStop is called in a background thread (because NoWait is used), CleanupTask must be scheduled back to the main thread, as in the original code.

Teradata - How to handle exceptions 'Call failed' in procedure?

All
I have a main procedure which is used to invoke other sub-procedures. I've added the 'DECLARE EXIT HANDLER FOR SQLEXCEPTION' in the main procedure. But when any exception raised in sub-procedure, the HANDLER in main procedure doesn't work.
So how can I catch exceptions generated in all sup-procedures?
vertion of my Teradata is 13.1, and the following is the simplified version of my code.
REPLACE PROCEDURE proc_main()
BEGIN
-- # Handl SQLException
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
insert log table.
END;
CALL proc_sub();
END;
Thanks!
Frank Liu
Are those errors already catched by exception handlers in the sub-procedures?
Only unhanded exceptions are propagated to the calling procedure.
You can use RESIGNAL to throw an error again.
Can you provide more details?

Exceptions on DBGrid editing

I have implemented an editable DBGrid. If a field is not filled correctly an exception is thrown and a message is shown, for example:
'08:00::00' is not a valid time
How can I catch those exceptions so that I can show messages written by me instead of the automatically generated ones? I would be thankful for any help.
As #teran pointed in his comment, the exception is raised by the TDataSet (or one of it's components) that is bind to the TDBGrid, or by the DB engine itself.
You can try to handle the TDataSet.OnPostError (see also OnUpdateError and OnEditError):
TDataSet.OnPostError: Occurs when an application attempts to modify or insert a record and
an exception is raised. Write an OnPostError event handler to handle
exceptions that occur when an attempt to post a record fails.
Note that you could always use Application.OnException global event handler to catch any EDBxxx exceptions in your application.
EDIT: The EConvertError exception is raised before any actual data modifications, or any Post operation by the TDateTimeField field i.e.:
0045af91 +085 Project1.exe SysUtils StrToDateTime <- EConvertError
004ab76a +042 Project1.exe Db TDateTimeField.SetAsString
004a9827 +007 Project1.exe Db TField.SetText
004a95d9 +029 Project1.exe Db TField.SetEditText
004d6448 +014 Project1.exe DBGrids TCustomDBGrid.UpdateData
004d087f +02b Project1.exe DBGrids TGridDataLink.UpdateData
004d599a +01a Project1.exe DBGrids TCustomDBGrid.MoveCol
StrToDateTime is throwing the exception inside TDateTimeField.SetAsString, not touching the data, and
the TDataSet.OnxxxError event handlers will not be fired at all.
So your choices are (test the application in release mode):
1.Intercept and handle EConvertError via Application.OnException.
2.Use TField.EditMask to restrict user input to a valid time format e.g. !90:00;1;_ or use inplace DateTimePicker editor inside your Grid. (and avoid catching this exception).
3.Override TDateTimeField: use persistent fields with your TDataSet and create an inter-poser class as such:
type
TDateTimeField = class(Db.TDateTimeField)
protected
procedure SetAsString(const Value: string); override;
end;
TForm1 = class(TForm)
...
procedure TDateTimeField.SetAsString(const Value: string);
begin
try
inherited SetAsString(Value);
except
on E: EConvertError do
begin
ShowMessage(E.Message);
Abort;
end;
end;
end;
If an exception is being raised then there should be two error messages being shown when run in the debugger. One of these will be caught by the debugger and the 2nd handled by the UI (when running your program as a user you will only see the 2nd).
The exception error message should have contain a string like
Appname.exe raised exception EExceptionName with message XXX
You will need to take note of EExceptionName.
Around the block of code creating the exception you will need to write
...
try
code that can cause the exception here
except
on e: EExceptionName do
begin
ShowMessage('Your apps nicer error message here');
end;
end;
note - if you don't make a call to exit; after handling an exception, your code will continue executing everything after your try..except block. Also, if there are many things that can cause the same error message in the same code block then you may not be able to write anything too specific. e.Message is a string that holds the message that is shown in the unhandled exception and may be useful to also present to the user.
Also try to move away from BDE - ADO is far better supported on modern systems.

Fatal application error not caught by MadExcept

I have an application which runs multiple threads. I use MadExcept to catch errors and debug it.
The problem is that sometimes after 2-3 hours of running, Windows shows a close program dialog. Why isn't that error handled by MadExcept ?
Threads are a special case. If you have an exception in a thread, it will not get handled by the global handler, and will usually kill your application. The solution is easy though, with madExcept. Just catch the exception, and tell MadExcept about it. It will log in the usual way, and you won't kill your thread.
uses
{$IFDEF MadExcept}
madExcept,
{$ENDIF}
procedure TMyThread.Execute;
begin
try
SetName;
// do your stuff
except
on errInfo : Exception do
begin
{$IFDEF MadExcept}
HandleException(etNormal, errInfo);
{$ENDIF}
end;
end;
end;
What I also do is have the thread set a "RunningOK" property to true when it starts, and the exception sets it to False. This way my control code can see that something went wrong, and handle that appropriately (either restart it, or report the error, etc)

Resources