简体   繁体   English

从C#中的Delphi DLL抛出异常

[英]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. 我有一个用Delphi编写的.DLL这个DLL有一个函数Divide(它接受两个整数作为参数)并返回它应该的值。

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 :)) 现在如果我使用以下参数'5,0'然后它会抛出DivideByZeroException(这是正确的:))

But when I call the same .DLL from C# it doesn't catch any exception at all. 但是当我从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)
    {

    }
}

You cannot hope to catch that exception outside the DLL. 你不能希望在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. 解决方案是修复DLL。 Catch the exception in the DLL and return an error code to indicate failure. 捕获DLL中的异常并返回错误代码以指示失败。 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: 关于你的p / invoke用法的一些副作用:

  1. There's no need to specify CharSet when none of the arguments contains text. 当没有参数包含文本时,不需要指定CharSet
  2. SetLastError = true is incorrect and should be removed. SetLastError = true不正确,应删除。 The function doesn't call SetLastError . 该函数不会调用SetLastError Consequently the call to Marshal.GetLastWin32Error() is erroneous and should be removed. 因此,对Marshal.GetLastWin32Error()的调用是错误的,应该删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM