* * *

Author Topic: Exceptional  (Read 2280 times)

christian1987

  • New member
  • *
  • Posts: 30
Exceptional
« on: November 04, 2015, 04:58:04 pm »
When I raise an Exception from inside FreePascal DLL, the Delphi EXE just says: Stack Overflow.

Is there a reason why?

raise Exception.create('guava');

GetMem

  • Hero Member
  • *****
  • Posts: 2419
Re: Exceptional
« Reply #1 on: November 04, 2015, 06:39:21 pm »
It's not safe to raise exceptions across the DLL boundary because of  the different memory management. To be more precise, it is possible if you use a shared memory manager like: ShareMem, SimpleShareMem, etc...

GetMem

  • Hero Member
  • *****
  • Posts: 2419
Re: Exceptional
« Reply #2 on: November 04, 2015, 07:54:04 pm »
What about raising exception with a callback function, cross platform way?
1 Library
Code: Pascal  [Select]
  1. library Test;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes,
  7.   SysUtils;
  8.  
  9. type
  10.   PError = ^TError;
  11.   TError = record
  12.     FErrorCode: Integer;
  13.     FErrorMessage: String;
  14.     //you can add other fields here, even managed types
  15.   end;
  16.  
  17.  TOnRaiseError = procedure(Error: PError); cdecl;
  18.  
  19. var
  20.   OnRaiseError: TOnRaiseError = nil;
  21.   Error: PError;
  22.  
  23. //this is the procedure where the exception is happening
  24. procedure Dummy;
  25. begin
  26.   try
  27.     //some unsafe codde
  28.   except
  29.     on E: Exception do
  30.     begin
  31.       Error^.FErrorCode := GetLastOSError;
  32.       Error^.FErrorMessage := E.Message;
  33.       if Assigned(OnRaiseError) then
  34.         OnRaiseError(Error);
  35.     end;
  36.   end;
  37. end;
  38.  
  39. procedure InitRaiseError(AOnRaiseError: TOnRaiseError; AError: PError); cdecl;
  40. begin
  41.   OnRaiseError := AOnRaiseError;
  42.   Error := AError;
  43. end;
  44.  
  45. exports
  46.   InitRaiseError;
  47.  
  48. begin
  49. end.                  
  50.  

2. Application

Code: Pascal  [Select]
  1.  
  2. //...
  3. implementation
  4. uses dynlibs,  LCLType;
  5.  
  6. type
  7.   PError = ^TError;
  8.   TError = record
  9.     FErrorCode: Integer;
  10.     FErrorMessage: String;
  11.     //you can add other fields here, even managed types
  12.   end;
  13.  
  14.   TOnRaiseError = procedure(Error: PError); cdecl;
  15.  
  16. var
  17.   Error: PError;
  18.   LibHandle: TLibHandle = NilHandle;
  19.  
  20. { TfMain }
  21.  
  22. //this will display the actual error received from the dll
  23. procedure CallbackRaiseError(Error: PError); cdecl;
  24. begin
  25.   ShowMessage('Error raised from the dll:' + sLineBreak +
  26.               'ErrorCode:    ' + IntToStr(Error^.FErrorCode) + sLineBreak +
  27.               'ErrorMessage: ' + Error^.FErrorMessage);
  28. end;
  29.  
  30. procedure TfMain.FormCreate(Sender: TObject);
  31. type
  32.   TInitRaiseError = procedure(AOnRaiseError: TOnRaiseError; AError: PError); cdecl;
  33. var
  34.   LibName: string;
  35.   FarProc: TFarProc;
  36.   InitRaiseError: TInitRaiseError;
  37. begin
  38.   New(Error);
  39.   LibName := ExtractFilePath(ParamStr(0)) + 'Test.' + SharedSuffix;
  40.   LibHandle := LoadLibrary(LibName);
  41.   if LibHandle <> NilHandle then
  42.   begin
  43.     FarProc := GetProcedureAddress(LibHandle, 'InitRaiseError');
  44.     if FarProc <> nil then
  45.     begin
  46.       InitRaiseError := TInitRaiseError(FarProc);
  47.       InitRaiseError(@CallbackRaiseError, Error);
  48.     end;
  49.   end;
  50. end;
  51.  
  52. procedure TfMain.FormDestroy(Sender: TObject);
  53. begin
  54.   Dispose(Error);
  55.   if LibHandle <> NilHandle then
  56.   begin
  57.     UnloadLibrary(LibHandle);
  58.     LibHandle := NilHandle;
  59.   end;
  60. end;
  61.  
  62. end.              
  63.  
  64.  

z505

  • New member
  • *
  • Posts: 38
  • think first, code after
Re: Exceptional
« Reply #3 on: May 12, 2017, 11:18:20 am »
It's not safe to raise exceptions across the DLL boundary because of  the different memory management. To be more precise, it is possible if you use a shared memory manager like: ShareMem, SimpleShareMem, etc...

Or the cmem unit (c memory manager), but I'm not sure delphi and fpc's exception mechanisms are the same (compatible). It would be theoretically better if you had a fpc dll, and and fpc exe, if you were going to try this, not a delphi exe with an fpc dll, or vice versa.

And as for the alternative solution: catch all the exceptions in the DLL itself (not the exe) and make the DLL return error codes, instead of throwing exceptions that the exe will catch. This is obnoxious and tedious, but seems to be the only safe solution for throwing exceptions in a dll. Simply catch them all and return an error code that the exe can deal with. Annoying, I know.

Sorry, standard thread necromancer apology applies.
think first, code after

Thaddy

  • Hero Member
  • *****
  • Posts: 4650
Re: Exceptional
« Reply #4 on: May 12, 2017, 11:27:12 am »
Or the cmem unit (c memory manager), but I'm not sure delphi and fpc's exception mechanisms are the same (compatible).
For win64 they are. For win32 you may have to enable it: compile the compiler and the rtl with OPT=-dTEST_WIN32_SEH
Still, raising exceptions inside a dll is a bit of nonsense unless you also handle them inside the dll. Better to return error codes.

It also shows a thorough misunderstanding of how a library should be architected, but that's a different story.
(Know of any C libraries that throw exceptions into main?, of course not! Unless the main programmer screwed up)
« Last Edit: May 12, 2017, 12:30:28 pm by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

Thaddy

  • Hero Member
  • *****
  • Posts: 4650
Re: Exceptional
« Reply #5 on: May 12, 2017, 12:34:07 pm »
What about raising exception with a callback function, cross platform way?
The exception frames would get lost, defeating the use of structured exception handling. Unless all frames trigger an individual callback. (E.g. something like the heaptrc behavior on Lazarus/windows looks similar)
On the plus side: it has good communication under most but not all circumstances.
I wonder if such a system would be stable. using SEH on win32 just about helps very little.
Bottom line: handle the exception inside the dll and return an error code if you can't recover.

Exception to the exceptions: COM will allow - almost - all exceptions to propagate to the main program apart from hardware failure and out-of -memory and provided a com memory manager is used besides SEH..
« Last Edit: May 12, 2017, 12:49:49 pm by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

Thaddy

  • Hero Member
  • *****
  • Posts: 4650
Re: Exceptional
« Reply #6 on: May 12, 2017, 03:08:50 pm »
Also note z505 remarks about cmem is not correct. cmem is simply a basic heap memory manager w/o any optimizations. It does not help with exception handling in any way.
A COM memory manager does that, because of COM marshaling. Therefor it is - very - slow as I found out during the memory manager shoot-out a couple of years ago. :D %)
OTOH: it is the simplest replacement for sharemem under Windows.
« Last Edit: May 12, 2017, 03:11:19 pm by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 5827
Re: Exceptional
« Reply #7 on: May 12, 2017, 03:21:23 pm »
Or the cmem unit (c memory manager), but I'm not sure delphi and fpc's exception mechanisms are the same (compatible).
For win64 they are. For win32 you may have to enable it: compile the compiler and the rtl with OPT=-dTEST_WIN32_SEH
Still, raising exceptions inside a dll is a bit of nonsense unless you also handle them inside the dll. Better to return error codes.

Note that while this allows one (e.g. Delphi) to clean up exceptions generated by the other (e.g. FPC), I'm not sure if allows one to read information stored in
the exception by the other.

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus