Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch exception thrown from Delphi DLL in C#

A problem I recently encountered is following.

I've a .DLL written in Delphi this DLL has a function Divide (which accepts two integers as parameter) and returns its value as it should.

function Divide( aFirstValue, aSecondValue : Integer ) : Double; stdcall;
begin
  result := aFirstValue / aSecondValue;
end;

Now if I use the following parameters '5, 0' then it throws a DivideByZeroException (which is correct :))

But when I call the same .DLL from C# it doesn't catch any exception at all.

[DllImport("DelphiDLL.DLL", EntryPoint = "Divide", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern float Divide(Int32 a, Int32 b);

private void Button_Click_2(object sender, System.EventArgs e)
{
    try
    {
       TB.Text += "Divide(a,b) = ";
       float temp;
       temp = Divide(Convert.ToInt32(aTB.Text), Convert.ToInt32(bTB.Text));

       Console.WriteLine(Marshal.GetLastWin32Error());

       TB.Text += Convert.ToString(temp) + "\r\n";
    }
    catch (DivideByZeroException eMsg)
    {

    }
}
like image 311
Blaatz0r Avatar asked Dec 25 '22 01:12

Blaatz0r


1 Answers

You cannot hope to catch that exception outside the DLL. One of the rules of this form of binary interop is that exceptions cannot be thrown across module boundaries.

The solution is to fix the DLL. Catch the exception in the DLL and return an error code to indicate failure. Indeed you should protect all entry points against throwing an exception. Don't just catch zero divide exceptions, catch them all and convert them into error code return values.

function CalcQuotient(a, b: Integer; out quotient: Double): Integer; stdcall;
begin
  try
    quotient := a / b;
    Result := 0;// you'd use a constant with a sensible name rather than a magic value
  except
    on E: Exception do begin
      Result := GetErrorCode(E);
      // where GetErrorCode is your function that converts exceptions into error codes
    end;
  end;
end;

Some asides regarding your p/invoke usage:

  1. There's no need to specify CharSet when none of the arguments contains text.
  2. SetLastError = true is incorrect and should be removed. The function doesn't call SetLastError. Consequently the call to Marshal.GetLastWin32Error() is erroneous and should be removed.
like image 150
David Heffernan Avatar answered Dec 27 '22 14:12

David Heffernan