1

显然,CLR 通过忽略异常并返回默认值来处理浮点异常。例如,在浮点溢出的情况下,CLR 返回 +Infinity,但不会引发异常。另一方面,Delphi 默认情况下会在溢出的情况下引发异常。这由 FPU 异常标志控制,您可以通过相应地设置异常标志使 Delphi 与 CLR 执行相同的操作。一切都很好。

我有一个用 Delphi 编写的数值库,它期望溢出作为异常引发。然后它相应地处理这些边缘情况。如果没有引发异常,它不知道该怎么做。现在,如果我在 COM dll 中使用该库并从托管代码中调用该 COM DLL,则不会将溢出作为异常引发,并且该库会返回不正确的结果。我已经通过将所有对库的调用包装在以下代码中来克服这个问题:

var
  Mask : TFPUExceptionMask;
begin
  Mask := Math.SetExceptionMask([exDenormalized, exUnderflow, exPrecision]);
  try
    //do my calls to the library
  finally
    Math.SetException(Mask);
  end;

这似乎有效,但我现在遇到了一个溢出的情况,CLR 似乎接管了控制权,而 Delphi 从来没有得到 EOverflow 异常。它只是没有那么远。它进入溢出异常循环(浮点溢出而不是 Delphi 生成的 EOverflow)并使应用程序崩溃。

解决这个问题的唯一方法是不使用上面的掩码,找出可能溢出的地方,并在这段代码中添加一些错误处理代码。这是一个例子:

f1 := Power(X, Y);
if f1 = Infinity then
  raise EOverflow.Create('Overflow');

这并不理想,因为我不太了解图书馆,无法猜测我可能会溢出的所有可能区域。但上述工作正常。

有人有这方面的经验吗?是否可以告诉 CLR 退出 FPU 异常并让 Delphi 处理它们?顺便说一句,如果我从 Excel VBA 调用相同的 COM DLL,则会出现相同的掩码问题,但使用上面的第一个修复,即强制调用掩码,工作正常。只是托管代码有问题。

所以这里有一个例子:

我创建了一个带有单个 COM 对象的 Delphi Active X 库 (DLL)。该对象有一个名为 TestOverflow 的方法,如下所示:

function TTestExceptions.TestOverflow : Double;
var
  Mask : TArithmeticExceptionMask;
begin
  Mask := GetExceptionMask;
  try
    Result := Power(600, 30000);
  except
    On EOverflow do
      Result := -1;
  end;
end;

然后我创建了三个客户端。一份用 Delphi 编写,一份用 EXCEL VBA 编写,一份用 C# 编写。每个客户都做同样的事情。创建 COM 对象的实例,并调用 TestOverflow。

a) 德尔福客户端

掩码为 [exDenormalized, exUnderflow, exPrecision]

抛出带有消息“浮点溢出”的异常 $C0000091,并将其作为 EOverflow 异常捕获,函数返回 -1。

b) Excel 客户端

掩码为 [exInvalidOp, exZeroDivide, exOverflow, exUnderflow, exPrecision]

不抛出异常,函数返回 +INF 作为结果。

c) C# 客户端

掩码为 [exInvalidOp, exZeroDivide, exOverflow, exUnderflow, exPrecision]

不抛出异常,函数返回 +INF 作为结果。

然后我更改函数以操作异常掩码:

function TTestExceptions.TestOverflow : Double;
var
  Mask : TArithmeticExceptionMask;
begin
  Mask := Math.SetExceptionMask([exDenormalized, exUnderflow, exPrecision]);
  try
    try
      Result := Power(600, 30000);
    except
      On EOverflow do
        Result := -1;
    end;
  finally
    Math.SetExceptionMask(Mask);
  end;
end;

这次我得到:

a) 德尔福客户端

抛出带有消息“浮点溢出”的异常 $C0000091,并将其作为 EOverflow 异常捕获,函数返回 -1。

b) Excel 客户端

抛出带有消息“浮点溢出”的异常 $C0000091,并将其作为 EOverflow 异常捕获,函数返回 -1。

c) C# 客户端

抛出带有消息“浮点溢出”的异常 $C0000091,但执行不会继续,而是在该行停止,并且似乎卡在该行并继续抛出相同的异常,直到它崩溃。

4

0 回答 0