7

What is the right way to handle exceptions thrown from inside a DLL in Delphi?

Something like this

on E : ESomeException do ...

or

if (E is ESomeException) then ...

fails, because of the separate type registries of the DLL and the main application.

jpfollenius
  • 16,456
  • 10
  • 90
  • 156
  • You should definitely look into ways to appropriately punish the author of the DLL. Hopefully that would in time solve the issue for all its users. – mghie Feb 12 '10 at 11:57
  • 1
    If you happen to have a link at hand to some article describing best practices for error handling in DLLs, please post it. – jpfollenius Feb 12 '10 at 12:21
  • 4
    I don't know an article, but it's quite simple really. If you want to use exceptions, write a BPL and tie your program to the same Delphi version. If you don't want to do this, and you write a DLL, then do not assume anything about the hosting environment, so exception MUST NOT cross modules. Have a top level exception handler in each exported function, and return an error code, just like COM does. See `OleCheck()` for an example of raising exceptions from error codes. NB: There were similar SO discussions regarding object sharing. But in the end rules for writing DLLs have to be obeyed. – mghie Feb 12 '10 at 13:12

5 Answers5

9

For pure DLL's exceptions are not allowed to cross the DLL boundary (like Deltics mentions) - no matter what language.

You get all sorts of trouble there, especially because you don't know which language, RTL, memory manager, etc, is on each side of the boundary.

So you are back to the classic error handling paradigm:

Instead of DLL's, you could use BPL packages (as Lars suggested): there you know that both sides will use the same RTL and memory manager.

Both packages and BPL usually give you a versioning nightmare anyway (too many degrees of freedom).

A more rigorous solution is to go for a monolithic executable; this solves both problems:

  • much easier versioning
  • guaranteed only one RTL and memory manager

--jeroen

PS: I've made this an additional answer because that allows for easier pasting of links.

Community
  • 1
  • 1
Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
5

The safest way is to not allow exceptions to "escape" from the DLL in the first place.

But if you have no control over the source of DLL and are therefore unable to ensure this, you can still test the exception class name:

if SameText(E.ClassName, 'ESomeException') then ...
Deltics
  • 22,162
  • 2
  • 42
  • 70
  • That's what I use as a workaround for now. Unfortunately it does not allow for catching parent exception classes. What is the right way to do error handling in a DLL? Error Codes? – jpfollenius Feb 12 '10 at 08:01
3

If you use runtime packages (at least rtlxx.bpl) for both your application and your dll, then both have the same type and it will work. Of course this limits the use of your dll to Delphi/BCB only.

Another solution is not using exceptions at all like Deltics suggest. Return error codes.

Or use COM. Then you can have exceptions and not limit your dll to Delphi only.

Lars Truijens
  • 42,837
  • 6
  • 126
  • 143
  • Thanks! Unfortunately runtime packages are not an option for us. And COM seems like too much overkill to me. What's wrong with exceptions in this case exactly (apart from the fact that the dynamic type check does not work)? – jpfollenius Feb 12 '10 at 08:25
  • Why are runtime packages not an option? With SimpleShareMem you are already tied to a specific Delphi/BCB version. And just using rtl70.bpl as a runtime package just gives you 1 extra dll to be distributed. – Lars Truijens Feb 12 '10 at 15:50
0

I think, it is agreed on that exceptions should never be allowed to escape from the DLL.

But sometimes you do not have control over a DLL and cannot avoid having trouble with exceptions.

We, for instance, had a problem with a function in an external DLL that was blocked by AV software settings ("ransomware protection") leading to access violations in Delphi.

The following code is working for us:

var
    O: TObject;
    Msg: AnsiString;   //type depending on DLL
begin
    try
        CallExternalDll(...);
    except
        //on E: Exception do might lead to access violations
        //because the exception might not be of type Exception
        O := ExceptObject;
        //Depending on the DLL this or an other cast might 
        //or might not be necessary:
        Msg := PAnsiChar(Exception(O).Message);

        raise Exception.Create(Msg);
    end;
end;
yonojoy
  • 5,486
  • 1
  • 31
  • 60
  • If it might not be of type Exception, you shouldn't cast it to Exception. – Lucas Belo Feb 06 '23 at 12:56
  • @LucasBelo in our example the external DLL seemed to be generated with Delphi so this cast was successful. Nobody can guarantee the structure of the content of ExceptObject. The only way to extract a message, is to guess what might be contained. In our example this code allowed to extract the original exception message. Other settings might need other interpretations of ExceptObject. I think the reinterpret cast here is okay - the worst case is a garbage message. – yonojoy Feb 07 '23 at 14:33
-3

This workaround seems to do it for me:

 function ExceptionMatch (Exc : Exception; ExcClass : TClass) : Boolean;

 var
    CurrClass           : TClass;

  begin
  CurrClass := Exc.ClassType;
  while (CurrClass <> nil) do
    begin
    if SameText (CurrClass.ClassName, ExcClass.ClassName) then
      Exit (True);
    CurrClass := CurrClass.ClassParent;
    end;
  Result := False;
  end;

I'm prepared for you to destroy this :)

What is wrong with this approach? What is potentially dangerous?

jpfollenius
  • 16,456
  • 10
  • 90
  • 156
  • Memory management, RTL versioning, classes that don't exist on both sides of the bounary. And that is only the beginning of your nightmare... – Jeroen Wiert Pluimers Feb 12 '10 at 10:51
  • I'm using `SimpleShareMem`, so memory management is not a problem here. I don't understand your other points. – jpfollenius Feb 12 '10 at 10:59
  • 2
    BTW: you downvote this and updvote Deltics (good) answer? I use the same approach as he suggests for the case that I can't influence the DLL itself. – jpfollenius Feb 12 '10 at 11:05