Can I resume execution from where the exception was thrown?

There is an example that illustrates my question:

procedure Test; begin try ShowMessage('1'); raise EWarning.Create(12345, 'Warning: something is happens!'); ShowMessage('2'); except on E: EWarning do if IWantToContinue(E.ErrorCode) then E.SkipThisWarning // shows '1' then '2' else E.StopExecution; // shows '1' end; end; function IWantToContinue(const ErrorCode: Integer): Boolean; begin //... end; 

I tried using something like this:

 asm jmp ExceptAddr end; 

but it will not work ...

Any ideas?

Thanks.

+7
source share
5 answers

No, It is Immpossible:

There are two types of exceptions: logical exceptions that are raised by the programmer using the raise command and external exceptions that are triggered by the CPU for various conditions: division by zero, stack overflow, access violation. For the first kind, logical exceptions, you cannot do anything, because they are part of the thread application. You cannot mess with a stream of third-party code, you cannot even fall into a stream of your own code.

External exceptions

Typically, they result from running a single CPU command when this command fails. In Delphi, they become available as EExternal descendants. The list includes access violations, division by zero, stack overflow, privileged instruction, and not many others. Theoretically, for some of these exceptions, it was possible to eliminate the causing exception condition and retry one CPU command, allowing the source code to continue as if an error had not occurred. For example, some access violations can be “fixed” by actually matching the RAM page to the address where the error occurred.

The SEH (Structured Exception) engine provides tools provided by Windows to deal with such errors that can be retried, and Delphi uses SEH under the hood. Unfortunately, Delphi does not disclose the necessary elements to make it easily accessible, so using them would be very difficult, if not impossible. However, for certain types of EExternal errors, smart Delphinians may try to write their own SEH code and make everything work. In this case, ask a new question that mentions the specific type of error you receive, as well as the steps you want to take to eliminate the error condition: you will receive either a working code or an individual explanation of why your idea will not work.

Logical exceptions raised with raise

Most exceptions fall into this category because most codes will check its entries before making potentially dangerous low-level material. For example, when trying to access an invalid index in a TList , the index will be checked, and an invalid index exception occurs before trying to access the requested index. Without verification, access to an invalid index will result in either the return of invalid data or an access violation. Both of these conditions will be very difficult to track for errors, so incorrect index exclusion is a very good thing. For the sake of this question, even if the code was allowed to access an invalid index, which led to an Access Violation, it would be impossible to “fix” the code and continue, because there is no way to guess what the correct index should be.

In other words, fixing “logical” exceptions does not work, it should not work, and it is insanely dangerous. If the error code is yours then you can simply rebuild it so as NOT to raise warnings exceptions. If this is not your code, then the continuation of the exception falls into the category of "insanely dangerous" (not to mention the technical impossibility). If you look at the code already written, ask yourself: will the code be correct if raise Exeption were replaced with ShowMessage ? The answer should be mainly "NO, the code would fail anyway" . For a very rare, very wrong case of third-party code that raises an exception for no good reason, you can ask for specific help to fix the code at runtime so that you NEVER raise an exception.

There may be some third-party code:

 function ThirdPartyCode(A, B: Integer): Integer; begin if B = 0 then raise Exception.Create('Division by zero is not possible, you called ThirdPartyCode with B=0!'); Result := A div B; end; 

It should be obvious that the continuation of this code after the exception will not allow the material to "heal" itself.

Third-party code may also look like this:

 procedure DoSomeStuff; begin if SomeCondition then begin // do useful stuff end else raise Exception.Create('Ooops.'); end; 

Where would this code continue? Obviously, this is not part of the "do usefull stuff" unless the code is specifically designed in this way.

These were, of course, simple examples, only scratching the surface. From a technical point of view, “continuing” after an exception, as you suggest, is much more complicated than going to the error address. The method call uses stack space to configure local variables. This space was released during the rollback process after an error occurred on the way to your exception handler. The finally blocks were executed, possibly allocating resources if necessary. Going to the source address will be very wrong, because the calling code no longer has what it expects on the stack, its local variables are no longer who they are.

If this is your code that throws an exception

Your code can be easily fixed. Use something like this:

 procedure Warning(const ErrorText:string); begin if not UserWantsToContinue(ErrorText) then raise Exception.Create(ErrorText); end; // in your raising code, replace: raise Exception.Create('Some Text'); // with: Warning('Some Text'); 
+6
source

AFAIK no. You should rebuild your code to something like

 procedure Test; begin ShowMessage('1'); try raise EWarning.Create(12345, 'Warning: something is happens!'); except on E: EWarning do if IWantToContinue(E.ErrorCode) then // shows '1' then '2' else raise; // shows '1' end; ShowMessage('2'); end; 
+3
source

In C ++, you can use the SEH __try/__except block, whose __except expression evaluates to EXCEPTION_CONTINUE_EXECUTION .

In Delphi, you cannot directly use SEH, AFAIK.

+1
source

BASICally, your code will not work because ExceptAddr is a function , not a variable . So, your code fragment changes as follows:

 {$O-} procedure TForm1.FormCreate(Sender: TObject); begin try OutputDebugString('entering'); raise Exception.Create('Error Message'); OutputDebugString('leaving'); except on E: Exception do begin OutputDebugString(PChar(Format('ExceptAddr = %p', [ExceptAddr]))); asm CALL ExceptAddr JMP EAX end; end; end; end; 
0
source

.. you can nest a few try-except and determine whether to continue in each exception:

 try try some code except ret := message('want to continue?'); if ret <> mrYes then exit; end; some other code to perform when no exception or user choose to continue except etc.. end; 
0
source

All Articles