从C#中的Delphi DLL抛出异常

Bla*_*z0r 4 c# delphi dll pinvoke dllimport

我最近遇到的一个问题是如下.

我有一个用Delphi编写的.DLL这个DLL有一个函数Divide(它接受两个整数作为参数)并返回它应该的值.

function Divide( aFirstValue, aSecondValue : Integer ) : Double; stdcall;
begin
  result := aFirstValue / aSecondValue;
end;
Run Code Online (Sandbox Code Playgroud)

现在如果我使用以下参数'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)
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 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;
Run Code Online (Sandbox Code Playgroud)

关于你的p/invoke用法的一些副作用:

  1. 没有必要指定CharSet何时没有参数包含文本.
  2. SetLastError = true是不正确的,应删除.该功能不会调用SetLastError.因此,呼叫Marshal.GetLastWin32Error()是错误的,应该被删除.

  • @ Blaatz0r也许有一些鬼鬼祟祟的方法来抓住他们,但我对此表示怀疑.无论如何,你不是要抛弃它们,所以不要! (2认同)