Delphi - Handling exceptions on its own constructor after it is thrown

Question: after I raise an exception, can I stop it for propagation from its own constructor? consider the following code:

unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TMyErrorClass = class(Exception) constructor Create(aMsg:String); end; TForm2 = class(TForm) procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form2: TForm2; implementation {$R *.dfm} procedure TForm2.FormCreate(Sender: TObject); begin // raise TMyErrorClass.Create('test'); end; { TMyErrorClass} constructor TMyErrorClass.Create(aMsg: String); begin {$IFDEF DEBUG} Showmessage(aMsg); {$ELSE} //here I want to 'kill' the exception {$ENDIF} end; end. 

After calling the flight, how can I end the Exception without adding try except / finally, where do I raise an exception?

LE: I have an application that has almost 2,000 raises like this ... and I'm trying to find an alternative solution for writing errors for it ....

+4
source share
5 answers

Once you enter the raise , there are only two ways to avoid the transition by eliminating this exception:

  • Pick up something else first. Inside the constructor of the exception to be raised, you can first create and create another exception. This will allow your program to ever reach the first raise .

  • Terminate the thread so that nothing starts. You can do this in various ways, including ExitThread , Halt and ExitProcess , which are pretty much equivalent in this context. Your program will no longer work, but at least you prevented its removal!

None of them will solve the problem that you are trying to solve, but they will do what you asked in your question. The bottom line is that all you are trying to do, suppress an exception after you have already initiated the exception mechanism, is the wrong approach. You cannot cancel the call, and you cannot exclude the exception.

+5
source

The way to stop the exception from spreading is to catch it with except . Note that you are not writing except where you are throwing an exception, but in the place where you want it to stop propagating.

However, you probably should not stop the exception that occurs inside the constructor from propagating outside this constructor. If you do so, your object may be partially constructed.

I believe that the correct solution to your problem is, like so many others, talking about throwing exceptions when you encounter errors and let them float to a high-level exception handler.

+3
source

When an instance of TMyErrorClass is created, it does not know whether it will be raised or not. You are asking to break the causal connection of something as fundamental as handling language exceptions. I will never do this in production code, because it will be a ticking time bomb. Enjoy (written in Delphi 6):

 type TMyClass = class(TObject) public Name: string; constructor Create; end; type REArguments = array[0..100] of DWORD; PREArguments = ^REArguments; type TRaiseExceptionProc = procedure (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PREArguments); stdcall; var RaiseExceptionProc_Old: TRaiseExceptionProc; procedure MyRaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PREArguments); stdcall; var raisedObject: TObject; begin if @RaiseExceptionProc_Old <> nil then begin RaiseExceptionProc := @RaiseExceptionProc_Old; RaiseExceptionProc_Old := nil; raisedObject := Pointer(lpArguments^[1]); if raisedObject is TMyClass then begin raisedObject.Free; end else begin TRaiseExceptionProc(RaiseExceptionProc)(dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments); end; end; end; constructor TMyClass.Create; begin inherited; Name := 'MyClass'; RaiseExceptionProc_Old := RaiseExceptionProc; RaiseExceptionProc := @MyRaiseException; end; procedure TForm1.Button1Click(Sender: TObject); begin raise TMyClass.Create; ShowMessage('Continue without interruption.'); end; 
+1
source

I know this is ugly on all accounts, but wanted to share. The code does not change the exception class, it looks like a global exception handler, except that it will not be raised, execution will continue, as if nothing had happened:

 procedure TestException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PDWORD); stdcall; var Arg: array of DWORD absolute lpArguments; begin {$IFDEF DEBUG} if (nNumberOfArguments > 1) and Assigned(lpArguments) and (TObject(Arg[1]).ClassName = TMyErrorClass.ClassName) then begin ShowMessage('Test'); TObject(Arg[1]).Free; Exit; end; {$ENDIF} windows.RaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments); end; procedure TForm1.FormCreate(Sender: TObject); begin RaiseExceptionProc := @TestException; .. 

Tested only with 32bit and D2007, I have no idea if this will work differently.

+1
source

Use Application.OnException to implement typical exception handling behavior at the top level of your application. For instance:

 Application.OnException := MainForm.DefaultExceptionHandler; procedure TMainForm.DefaultExceptionHandler (Sender : TObject; E : Exception); begin {$IfDef DEBUG} ShowMessage (E.Message); {$EndIf} end; 

A constant normal flow of control after an exception is a bad IMHO idea.

0
source

Source: https://habr.com/ru/post/1386693/


All Articles