在C#中捕获从Delphi DLL抛出的异常

4
我最近遇到的问题如下。
我有一个用Delphi编写的.DLL文件,这个DLL文件有一个函数Divide(它接受两个整数作为参数),并返回相除后的值,一切正常。
function Divide( aFirstValue, aSecondValue : Integer ) : Double; stdcall;
begin
  result := aFirstValue / aSecondValue;
end;

现在,如果我使用以下参数'5, 0',那么它会抛出DivideByZeroException异常(这是正确的:)

但是当我从C#调用相同的.DLL时,它根本没有捕获任何异常。

[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)
    {

    }
}

是的,在 C# 中它显示为无穷大,因此返回了一个值,但它也可能是0.0.....第n个。 - Blaatz0r
除数为零时,不调用除法方法怎么样?反正你知道输入无效了,对吧? - Sriram Sakthivel
1
@SriramSakthivel - 这可以解决这个问题,但不能解决异常为什么没有返回给调用的C#方法的问题。 - user1666620
@user1666620 我并没有说这样做会解决问题。如果你知道事情出了问题,为什么还要继续呢? - Sriram Sakthivel
1
@SriramSakthivel 不要因为使用简单的例子来演示问题而嘲笑OP。他的问题已经清楚地陈述了。至少他努力提供简单明了的代码示例,这比许多问题都期望得到帮助但没有付出任何努力去梳理问题要好得多。(我已经编辑了标题,删除了特定异常的引用,因为根据问题的主体内容,这显然不重要。) - Disillusioned
显示剩余5条评论
1个回答

10

你不能指望在 DLL 外部捕获该异常。此二进制互操作形式的规则之一是,不能在模块边界抛出异常。

解决方法是修复 DLL。在 DLL 中捕获异常并返回错误代码来指示失败。确实,您应该保护所有入口点以防止抛出异常。不仅仅是捕获零除异常,捕获它们全部并将其转换为错误代码返回值。

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;

关于您的p/invoke使用,有一些需要注意的方面:

  1. 如果没有任何参数包含文本,则无需指定CharSet
  2. SetLastError = true是不正确的,应该将其删除。这个函数不会调用SetLastError。因此,调用Marshal.GetLastWin32Error()是错误的,并且应该被删除。

David,你说“你不能指望”,这是意味着这根本不可能还是还有一线希望?我只是好奇。我会查看错误代码。 - Blaatz0r
2
@Blaatz0r 或许有一些狡猾的方法可以抓住它们,但我怀疑。无论如何,你不应该扔它们! - David Heffernan
1
@Blaatz0r 在 Delphi 中引发的异常将是 EZeroDivide。这是一个特定于 Delphi 的对象。C# 将无法正确使用它。是的,一些标准异常类型 可以 从 Delphi 映射到 C#,例如 EZeroDivide --> DivideByZeroException,但这仍然会留下自定义异常类型未被考虑。它们只能通过某些最低公共分母进行映射。一些互操作框架可能能够在 Delphi 和 C# 之间传递异常。但在底层,它们将执行与 David 的答案中演示的相同类型的操作。 - Disillusioned
@CraigYoung,你对Delphi异常(EZeroDivide)的看法是正确的,当我想到C#变体时,我就写了DivideByZeroException :) - Blaatz0r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接